stressapptest源码剖析:默认参数和参数解析
一、简介
stressapptest是一个用于对计算机系统进行压力测试的工具,它可以模拟多种场景下的CPU、内存、磁盘、网络等资源的高负载情况,用于评估系统的稳定性和性能表现。本系列文章将对stressapptest的源代码进行深入剖析,本篇重点关注其默认参数的设置和参数解析过程,以解析其内部运行机制和实现原理。
随着计算机系统的日益复杂和性能需求的不断提升,系统稳定性和高负载下的性能表现成为了至关重要的考量因素。而stressapptest作为一个优秀的压力测试工具,能够帮助有效地评估硬件设备和系统软件在高负载情况下的表现。因此,深入了解stressapptest的实现细节和参数设置机制,将有助于更好地理解其工作原理,提高对系统性能测试的专业水平。
本系列文章将全面剖析stressapptest的源代码,从默认参数的设置到参数解析的过程,逐步揭示其内部运行机制和设计思路。读者将通过本系列文章深入了解stressapptest的内部工作原理,拓展对系统压力测试工具的理解,为系统性能的评估和优化提供参考和帮助。
接下来的章节将详细讲解stressapptest的默认参数设置和参数解析过程,以及相关的代码解析和分析。同时,我们将结合实际代码,带领读者逐步理解stressapptest的内部机制和实现细节。
二、Sat构造函数初始化默认参数
stressapptest源代码有一个名为Sat
的类,它的构造函数初始化了该类的各个成员变量的默认值。这些默认值包括运行时间、页面长度、磁盘页面数、大小、保留大小、错误注入标志等等。此外,它还初始化了一些与缓存一致性和CPU频率相关的数据。同时,设置了一些线程相关的默认值和一些其他参数的默认值,如锁、区域掩码等等。最后,它初始化了一些文件读取和写入相关的参数。
源代码:
// Constructor and destructor.
Sat::Sat() {
// Set defaults, command line might override these.
runtime_seconds_ = 20;// 默认运行时间,单位为秒
page_length_ = kSatPageSize;// 设置每个内存块的长度为1024LL*1024LL
disk_pages_ = kSatDiskPage;// 设置每个临时文件的页数为8
pages_ = 0;
size_mb_ = 0;
size_ = size_mb_ * kMegabyte;// 设置默认的测试内存为 0 * (1024LL*1024LL)
reserve_mb_ = 0;
min_hugepages_mbytes_ = 0;
freepages_ = 0;
paddr_base_ = 0;
channel_hash_ = kCacheLineSize;// 地址位的掩码,64。
channel_width_ = 64;
user_break_ = false;
verbosity_ = 8;
Logger::GlobalLogger()->SetVerbosity(verbosity_);//设置日志打印等级,大于8的等级将被忽略。
print_delay_ = 10;
strict_ = 1;
warm_ = 0;
run_on_anything_ = 0;
use_logfile_ = false;
logfile_ = 0;
log_timestamps_ = true;
// Detect 32/64 bit binary.
void *pvoid = 0;
address_mode_ = sizeof(pvoid) * 8;
error_injection_ = false;
crazy_error_injection_ = false;
max_errorcount_ = 0; // Zero means no early exit.
stop_on_error_ = false;
error_poll_ = true;
findfiles_ = false;
do_page_map_ = false;
page_bitmap_ = 0;
page_bitmap_size_ = 0;
// Cache coherency data initialization.
cc_test_ = false; // Flag to trigger cc threads.
cc_cacheline_count_ = 2; // Two datastructures of cache line size.
cc_cacheline_size_ = 0; // Size of a cacheline (0 for auto-detect).
cc_inc_count_ = 1000; // Number of times to increment the shared variable.
cc_cacheline_data_ = 0; // Cache Line size datastructure.
// Cpu frequency data initialization.
cpu_freq_test_ = false; // Flag to trigger cpu frequency thread.
cpu_freq_threshold_ = 0; // Threshold, in MHz, at which a cpu fails.
cpu_freq_round_ = 10; // Round the computed frequency to this value.
sat_assert(0 == pthread_mutex_init(&worker_lock_, NULL));
file_threads_ = 0;
net_threads_ = 0;
listen_threads_ = 0;
// Default to autodetect number of cpus, and run that many threads.
memory_threads_ = -1;
invert_threads_ = 0;
fill_threads_ = 8;
check_threads_ = 0;
cpu_stress_threads_ = 0;
disk_threads_ = 0;
total_threads_ = 0;
use_affinity_ = true;
region_mask_ = 0;
region_count_ = 0;
for (int i = 0; i < 32; i++) {
region_[i] = 0;
}
region_mode_ = 0;
errorcount_ = 0;
statuscount_ = 0;
valid_ = 0;
empty_ = 0;
finelock_q_ = 0;
// Default to use fine-grain lock for better performance.
pe_q_implementation_ = SAT_FINELOCK;
os_ = 0;
patternlist_ = 0;
logfilename_[0] = 0;
read_block_size_ = 512;
write_block_size_ = -1;
segment_size_ = -1;
cache_size_ = -1;
blocks_per_segment_ = -1;
read_threshold_ = -1;
write_threshold_ = -1;
non_destructive_ = 1;
monitor_mode_ = 0;
tag_mode_ = 0;
random_threads_ = 0;
pause_delay_ = 600;
pause_duration_ = 15;
}
相关的参数定义:
// Define handy constants here
static const int kTicksPerSec = 100;
static const int kMegabyte = (1024LL*1024LL);
static const int kSatDiskPageMax = 32;
static const int kSatDiskPage = 8;
static const int kSatPageSize = (1024LL*1024LL);
static const int kCacheLineSize = 64;
static const uint16_t kNetworkPort = 19996;
三、Sat的参数解析ParseArgs()
默认参数可以被命令行参数替换。源代码:
#define ARG_KVALUE(argument, variable, value) \
if (!strcmp(argv[i], argument)) { \
variable = value; \
continue; \
}
#define ARG_IVALUE(argument, variable) \
if (!strcmp(argv[i], argument)) { \
i++; \
if (i < argc) \
variable = strtoull(argv[i], NULL, 0); \
continue; \
}
#define ARG_SVALUE(argument, variable) \
if (!strcmp(argv[i], argument)) { \
i++; \
if (i < argc) \
snprintf(variable, sizeof(variable), "%s", argv[i]); \
continue; \
}
// Configures SAT from command line arguments.
// This will call exit() given a request for
// self-documentation or unexpected args.
bool Sat::ParseArgs(int argc, char **argv) {
int i;
uint64 filesize = page_length_ * disk_pages_;
// Parse each argument.
for (i = 1; i < argc; i++) {
// Switch to fall back to corase-grain-lock queue. (for benchmarking)
ARG_KVALUE("--coarse_grain_lock", pe_q_implementation_, SAT_ONELOCK);
// Set number of megabyte to use.
ARG_IVALUE("-M", size_mb_);
// Specify the amount of megabytes to be reserved for system.
ARG_IVALUE("--reserve_memory", reserve_mb_);
// Set minimum megabytes of hugepages to require.
ARG_IVALUE("-H", min_hugepages_mbytes_);
// Set number of seconds to run.
ARG_IVALUE("-s", runtime_seconds_);
// Set number of memory copy threads.
ARG_IVALUE("-m", memory_threads_);
// Set number of memory invert threads.
ARG_IVALUE("-i", invert_threads_);
// Set number of check-only threads.
ARG_IVALUE("-c", check_threads_);
// Set number of cache line size datastructures.
ARG_IVALUE("--cc_inc_count", cc_inc_count_);
// Set number of cache line size datastructures
ARG_IVALUE("--cc_line_count", cc_cacheline_count_);
// Override the detected or assumed cache line size.
ARG_IVALUE("--cc_line_size", cc_cacheline_size_);
// Flag set when cache coherency tests need to be run
ARG_KVALUE("--cc_test", cc_test_, true);
// Set when the cpu_frequency test needs to be run
ARG_KVALUE("--cpu_freq_test", cpu_freq_test_, true);
// Set the threshold in MHz at which the cpu frequency test will fail.
ARG_IVALUE("--cpu_freq_threshold", cpu_freq_threshold_);
// Set the rounding value for the cpu frequency test. The default is to
// round to the nearest 10s value.
ARG_IVALUE("--cpu_freq_round", cpu_freq_round_);
// Set number of CPU stress threads.
ARG_IVALUE("-C", cpu_stress_threads_);
// Set logfile name.
ARG_SVALUE("-l", logfilename_);
// Verbosity level.
ARG_IVALUE("-v", verbosity_);
// Chatty printout level.
ARG_IVALUE("--printsec", print_delay_);
// Turn off timestamps logging.
ARG_KVALUE("--no_timestamps", log_timestamps_, false);
// Set maximum number of errors to collect. Stop running after this many.
ARG_IVALUE("--max_errors", max_errorcount_);
// Set pattern block size.
ARG_IVALUE("-p", page_length_);
// Set pattern block size.
ARG_IVALUE("--filesize", filesize);
// NUMA options.
ARG_KVALUE("--no_affinity", use_affinity_, false);
ARG_KVALUE("--local_numa", region_mode_, kLocalNuma);
ARG_KVALUE("--remote_numa", region_mode_, kRemoteNuma);
// Autodetect tempfile locations.
ARG_KVALUE("--findfiles", findfiles_, 1);
// Inject errors to force miscompare code paths
ARG_KVALUE("--force_errors", error_injection_, true);
ARG_KVALUE("--force_errors_like_crazy", crazy_error_injection_, true);
if (crazy_error_injection_)
error_injection_ = true;
// Stop immediately on any arror, for debugging HW problems.
ARG_KVALUE("--stop_on_errors", stop_on_error_, 1);
// Don't use internal error polling, allow external detection.
ARG_KVALUE("--no_errors", error_poll_, 0);
// Never check data as you go.
ARG_KVALUE("-F", strict_, 0);
// Warm the cpu as you go.
ARG_KVALUE("-W", warm_, 1);
// Allow runnign on unknown systems with base unimplemented OsLayer
ARG_KVALUE("-A", run_on_anything_, 1);
// Size of read blocks for disk test.
ARG_IVALUE("--read-block-size", read_block_size_);
// Size of write blocks for disk test.
ARG_IVALUE("--write-block-size", write_block_size_);
// Size of segment for disk test.
ARG_IVALUE("--segment-size", segment_size_);
// Size of disk cache size for disk test.
ARG_IVALUE("--cache-size", cache_size_);
// Number of blocks to test per segment.
ARG_IVALUE("--blocks-per-segment", blocks_per_segment_);
// Maximum time a block read should take before warning.
ARG_IVALUE("--read-threshold", read_threshold_);
// Maximum time a block write should take before warning.
ARG_IVALUE("--write-threshold", write_threshold_);
// Do not write anything to disk in the disk test.
ARG_KVALUE("--destructive", non_destructive_, 0);
// Run SAT in monitor mode. No test load at all.
ARG_KVALUE("--monitor_mode", monitor_mode_, true);
// Run SAT in address mode. Tag all cachelines by virt addr.
ARG_KVALUE("--tag_mode", tag_mode_, true);
// Dump range map of tested pages..
ARG_KVALUE("--do_page_map", do_page_map_, true);
// Specify the physical address base to test.
ARG_IVALUE("--paddr_base", paddr_base_);
// Specify the frequency for power spikes.
ARG_IVALUE("--pause_delay", pause_delay_);
// Specify the duration of each pause (for power spikes).
ARG_IVALUE("--pause_duration", pause_duration_);
// Disk device names
if (!strcmp(argv[i], "-d")) {
i++;
if (i < argc) {
disk_threads_++;
diskfilename_.push_back(string(argv[i]));
blocktables_.push_back(new DiskBlockTable());
}
continue;
}
// Set number of disk random threads for each disk write thread.
ARG_IVALUE("--random-threads", random_threads_);
// Set a tempfile to use in a file thread.
if (!strcmp(argv[i], "-f")) {
i++;
if (i < argc) {
file_threads_++;
filename_.push_back(string(argv[i]));
}
continue;
}
// Set a hostname to use in a network thread.
if (!strcmp(argv[i], "-n")) {
i++;
if (i < argc) {
net_threads_++;
ipaddrs_.push_back(string(argv[i]));
}
continue;
}
// Run threads that listen for incoming SAT net connections.
ARG_KVALUE("--listen", listen_threads_, 1);
if (CheckGoogleSpecificArgs(argc, argv, &i)) {
continue;
}
ARG_IVALUE("--channel_hash", channel_hash_);
ARG_IVALUE("--channel_width", channel_width_);
if (!strcmp(argv[i], "--memory_channel")) {
i++;
if (i < argc) {
char *channel = argv[i];
channels_.push_back(vector<string>());
while (char* next = strchr(channel, ',')) {
channels_.back().push_back(string(channel, next - channel));
channel = next + 1;
}
channels_.back().push_back(string(channel));
}
continue;
}
// Default:
PrintVersion();
PrintHelp();
if (strcmp(argv[i], "-h") && strcmp(argv[i], "--help")) {
printf("\n Unknown argument %s\n", argv[i]);
bad_status();
exit(1);
}
// Forget it, we printed the help, just bail.
// We don't want to print test status, or any log parser stuff.
exit(0);
}
Logger::GlobalLogger()->SetVerbosity(verbosity_);
// Update relevant data members with parsed input.
// Translate MB into bytes.
size_ = static_cast<int64>(size_mb_) * kMegabyte;
// Set logfile flag.
if (strcmp(logfilename_, ""))
use_logfile_ = true;
// Checks valid page length.
if (page_length_ &&
!(page_length_ & (page_length_ - 1)) &&
(page_length_ > 1023)) {
// Prints if we have changed from default.
if (page_length_ != kSatPageSize)
logprintf(12, "Log: Updating page size to %d\n", page_length_);
} else {
// Revert to default page length.
logprintf(6, "Process Error: "
"Invalid page size %d\n", page_length_);
page_length_ = kSatPageSize;
return false;
}
// Set disk_pages_ if filesize or page size changed.
if (filesize != static_cast<uint64>(page_length_) *
static_cast<uint64>(disk_pages_)) {
disk_pages_ = filesize / page_length_;
if (disk_pages_ == 0)
disk_pages_ = 1;
}
// Validate memory channel parameters if supplied
if (channels_.size()) {
if (channels_.size() == 1) {
channel_hash_ = 0;
logprintf(7, "Log: "
"Only one memory channel...deactivating interleave decoding.\n");
} else if (channels_.size() > 2) {
logprintf(6, "Process Error: "
"Triple-channel mode not yet supported... sorry.\n");
bad_status();
return false;
}
for (uint i = 0; i < channels_.size(); i++)
if (channels_[i].size() != channels_[0].size()) {
logprintf(6, "Process Error: "
"Channels 0 and %d have a different count of dram modules.\n", i);
bad_status();
return false;
}
if (channels_[0].size() & (channels_[0].size() - 1)) {
logprintf(6, "Process Error: "
"Amount of modules per memory channel is not a power of 2.\n");
bad_status();
return false;
}
if (channel_width_ < 16
|| channel_width_ & (channel_width_ - 1)) {
logprintf(6, "Process Error: "
"Channel width %d is invalid.\n", channel_width_);
bad_status();
return false;
}
if (channel_width_ / channels_[0].size() < 8) {
logprintf(6, "Process Error: Chip width x%d must be x8 or greater.\n",
channel_width_ / channels_[0].size());
bad_status();
return false;
}
}
// Print each argument.
for (int i = 0; i < argc; i++) {
if (i)
cmdline_ += " ";
cmdline_ += argv[i];
}
return true;
}
stressapptest源代码定义了一个名为 Sat::ParseArgs
的成员函数,用于解析命令行参数并相应地配置名为 Sat
的类的实例。函数接受两个参数:argc
代表命令行参数的数量,argv
是一个字符指针数组,包含每个参数的字符串表示。
在代码中,有几个主要的部分需要特别关注:
-
变量初始化:在函数的开始部分,初始化了局部变量
i
(用于循环计数)和filesize
(基于私有成员page_length_
和disk_pages_
计算大小)。 -
解析循环:代码通过一个
for
循环遍历所有参数。在循环内部,使用多个宏来处理不同类型的命令行参数:ARG_IVALUE
:用于处理带有整数值的参数。ARG_KVALUE
:用于处理带有特定标志(键值对)的参数。ARG_SVALUE
:用于处理带有字符串值的参数。
这些宏是私有宏,用于读取或设置类的成员变量。
-
错误处理和帮助信息:如果遇到未知的参数或者用户明确要求帮助(通过
-h
或--help
参数),则调用PrintVersion()
和PrintHelp()
函数打印版本信息和帮助信息。如果参数未知且不是请求帮助,程序将打印错误信息并退出(使用exit(1)
)。 -
特殊参数处理:对于像
-d
、-f
和-n
这样的参数,代码采用特殊的处理逻辑。这些参数期待一个额外的值,该值紧跟在参数后面。例如,-d
参数处理磁盘线程和文件名。 -
后期验证和设置:在参数解析循环之后,函数执行几项验证和设置任务,这些任务需要所有参数都已经解析完毕才能进行。例如,程序转换从命令行读取的兆字节值到字节数,验证页面长度是否有效,以及确保磁盘页面的计算是基于任何可能发生变化的文件大小。
-
内存通道验证:如果提供了内存通道信息,函数会检查通道数量、DRAM模块的数量是否为2的幂,以及通道宽度是否有效。
-
命令行字符串构建:代码重新遍历所有参数,将它们拼接成一个完整的命令行字符串,并将其存储在
cmdline_
成员变量中。
ParseArgs
函数是一个典型的命令行参数解析器。
总结
这篇文章对stressapptest的源码进行了深入剖析,重点讨论了默认参数和参数解析的相关内容。对stressapptest中的参数解析进行了分析,阐述了参数解析的原理和实现方式,以及不同参数对测试效果的影响。