stressapptest源码剖析:主函数main解析和sat类头文件分析

主函数main解析和sat类头文件分析

  • 一、简介
  • 二、入口函数main.cc剖析
  • 三、SAT压力测试对象接口和数据结构
  • 总结

    一、简介

    stressapptest(简称SAT)是一种用于在Linux系统上测试系统稳定性和可靠性的工具,通过产生CPU、内存、磁盘等各种负载来测试系统的稳定性。

    本文主要剖析入口函数main()的流程,以及stressapptest的核心类Sat的头文件定义。从简单开始,一步步分析各个功能模块。

    二、入口函数main.cc剖析

    main.cc的代码块如下,主打一个简洁。通过注释的方式剖析大致流程。

    #include "sattypes.h"
    #include "sat.h"
    // 入口函数。
    int main(int argc, char **argv) { // Sat是stressapptest的核心类,通过SatFactory返回创建的对象指针。
      Sat *sat = SatFactory();
      if (sat == NULL) { logprintf(0, "Process Error: failed to allocate Sat object\n");
        return 255;
      }
      // 创建sat对象后,必须先解析命令行参数
      if (!sat->ParseArgs(argc, argv)) { logprintf(0, "Process Error: Sat::ParseArgs() failed\n");
        sat->bad_status();
      } else if (!sat->Initialize()) {// 接着进行相关的初始化
        logprintf(0, "Process Error: Sat::Initialize() failed\n");
        sat->bad_status();
      } else if (!sat->Run()) {// 初始化完成后即可运行压力测试
        logprintf(0, "Process Error: Sat::Run() failed\n");
        sat->bad_status();
      }
      // 测试完成后通过这个打印结果
      sat->PrintResults();
      // 进行一些清理工作
      if (!sat->Cleanup()) { logprintf(0, "Process Error: Sat::Cleanup() failed\n");
        sat->bad_status();
      }
      // 检查Sat的状态,以便返回给终端方便用户了解状态
      int retval;
      if (sat->status() != 0) { logprintf(0, "Process Error: Fatal issue encountered. See above logs for "
                  "details.\n");
        retval = 1;
      } else if (sat->errors() != 0) { retval = 1;
      } else { retval = 0;
      }
      // 释放Sat类的内存
      delete sat;
      return retval;
    }
    

    执行流程图:

    SatFactory()的实现在sat_factory.cc中,声明在sat.h里。非常的简单,就是new一个对象,然后返回。

    #include "sat.h"  // NOLINT
    Sat *SatFactory() { return new Sat();
    }
    

    三、SAT压力测试对象接口和数据结构

    这是一个为系统级压力测试和分析而设计的综合压力测试类。这里先了解功能函数接口,实现细节后面再分析。

    class Sat代码的简单分析:

    1. 代码定义了一个名为Sat的类,其中包含各种成员函数和数据成员。
    2. 有一个构造函数Sat()、一个析构函数~Sat()和几个成员函数来解析参数、初始化、运行测试、打印结果和清理。
    3. 类还包含获取和返回空页和满页的函数、访问器函数和操作错误注入的函数。
    4. 包含初始化、线程管理、分析和报告函数。
    5. 数据成员用来保存配置参数、控制标志、内存和测试配置、资源和结果。
    6. 代码使用了各种c++特性,如enmu类型、vector 和 map。
    7. 利用多线程使用pthreads,并包含特定的方法不同类型的压力测试,如内存,文件IO,网络IO,磁盘IO, CPU压力,和缓存一致性测试。
    8. 类定义并使用多个队列结构进行页面管理,它同时使用单锁和细锁队列实现。
    9. 包含各种不允许的复制和赋值操作。
    // sat.h : sat stress test object interface and data structures
    #ifndef STRESSAPPTEST_SAT_H_
    #define STRESSAPPTEST_SAT_H_
    #include #include #include #include // This file must work with autoconf on its public version,
    // so these includes are correct.
    #include "finelock_queue.h"
    #include "queue.h"
    #include "sattypes.h"
    #include "worker.h"
    #include "os.h"
    // SAT stress test class.
    class Sat { public:
      // Enum for page queue implementation switch.
      // 用于页面队列的单锁和细锁模式切换。SAT_ONELOCK和SAT_FINELOCK有不同的实现方式。
      enum PageQueueType { SAT_ONELOCK, SAT_FINELOCK };
      Sat();
      virtual ~Sat();
      // Read configuration from arguments. Called first.
      // 从命令行参数中读取配置参数,这个必须在所有的操作前调用。
      bool ParseArgs(int argc, char **argv);
      virtual bool CheckGoogleSpecificArgs(int argc, char **argv, int *i);
      // Initialize data structures, subclasses, and resources,
      // based on command line args.
      // Called after ParseArgs().
      // 根据命令行参数初始化数据结构、子类和资源。在ParseArgs()之后调用。
      bool Initialize();
      // Execute the test. Initialize() and ParseArgs() must be called first.
      // This must be called from a single-threaded program.
      // 执行测试。Initialize()和ParseArgs()必须先调用;而且不能被多线程或多次调用。
      bool Run();
      // Pretty print result summary.
      // Called after Run().
      // Return value is success or failure of the SAT run, *not* of this function!
      // 打印结果概括。
      bool PrintResults();
      // Pretty print version info.
      // 打印版本信息
      bool PrintVersion();
      // Pretty print help.
      // 打印帮助信息
      virtual void PrintHelp();
      // Clean up allocations and resources.
      // Called last.
      // 清理分配和资源。最后才调用。
      bool Cleanup();
      // Abort Run().  Only for use by Run()-installed signal handlers.
      // 中止运行。仅供Run()使用,而且安装了信号处理才能使用。
      void Break() { user_break_ = true; }
      // Fetch and return empty and full pages into the empty and full pools.
      bool GetValid(struct page_entry *pe);             // 获取任何有效的page
      bool PutValid(struct page_entry *pe);             // 放置有效page,
      bool GetEmpty(struct page_entry *pe);             // 获取带有任何tag的空page
      bool PutEmpty(struct page_entry *pe);             // 放置带有任何tag的空page
      bool GetValid(struct page_entry *pe, int32 tag);  // 获取任何有效的page,并且指定tag
      bool GetEmpty(struct page_entry *pe, int32 tag);  // 获取指定tag的空page
      // Accessor functions.
      // 一些获取成员变量值的函数。
      int verbosity() const { return verbosity_; }
      int logfile() const { return logfile_; }
      int page_length() const { return page_length_; }
      int disk_pages() const { return disk_pages_; }
      int strict() const { return strict_; }
      int tag_mode() const { return tag_mode_; }
      int status() const { return statuscount_; }
      void bad_status() { statuscount_++; }
      int errors() const { return errorcount_; }
      int warm() const { return warm_; }
      bool stop_on_error() const { return stop_on_error_; }
      bool use_affinity() const { return use_affinity_; }
      int32 region_mask() const { return region_mask_; }
      // Semi-accessor to find the "nth" region to avoid replicated bit searching..
      // 查找“第n”区域以避免重复bit位搜索。
      int32 region_find(int32 num) const { for (int i = 0; i < 32; i++) { if ((1 << i) & region_mask_) { if (num == 0)
              return i;
            num--;
          }
        }
        return 0;
      }
      // Causes false errors for unittesting.
      // Setting to "true" causes errors to be injected.
      // 造成单元测试错误。设置为“true”会导致注入错误。
      void set_error_injection(bool errors) { error_injection_ = errors; }
      bool error_injection() const { return error_injection_; }
     protected:
      // Opens log file for writing. Returns 0 on failure.
      // 打开并初始化log文件
      bool InitializeLogfile();
      // Checks for supported environment. Returns 0 on failure.
      // 检查环境是否已知并且可以安全运行。
      bool CheckEnvironment();
      // Allocates size_ bytes of test memory.
      // 分配要在其上运行测试的内存
      bool AllocateMemory();
      // Initializes datapattern reference structures.
      // 设置对data pattern的访问
      bool InitializePatterns();
      // Initializes test memory with datapatterns.
      // 初始化页面列表,并用datapatterns填充页面。
      bool InitializePages();
      // Start up worker threads
      // 启动SAT的任务线程.
      virtual void InitializeThreads();
      // Spawn worker threads.
      // 生成工作线程。
      void SpawnThreads();
      // Reap worker threads.
      // 通知并获得工作线程
      void JoinThreads();
      // Run bandwidth and error analysis.
      // 处理工作线程数据获取带宽信息和错误结果。可以在这里添加更多的方法。
      virtual void RunAnalysis();
      // Delete worker threads.
      // 删除已使用的工作线程对象。
      void DeleteThreads();
      // Return the number of cpus in the system.
      // 返回机器中实际存在的cpu数量。
      int CpuCount();
      // Return the worst-case (largest) cache line size of the system.
      // 返回机器中实际存在的各级缓存的最坏情况(最大)缓存行大小。
      int CacheLineSize();
      // Read int values from kernel file system e.g. sysfs
        // 从内核文件系统(例如sysfs)中读取int值
      int ReadInt(const char *filename, int *value);
      // Collect error counts from threads.
      // 获取所有线程的总错误计数。
      int64 GetTotalErrorCount();
      // Command line arguments.
      // 命令行参数字符串
      string cmdline_;
      // Memory and test configuration.
      // 内存和测试配置信息
      int runtime_seconds_;               // Seconds to run.运行的时间(秒)
      int page_length_;                   // Length of each memory block.每个内存块的长度
      int64 pages_;                       // Number of memory blocks.内存块的数量
      int64 size_;                        // Size of memory tested, in bytes.测试内存的大小,以字节为单位
      int64 size_mb_;                     // Size of memory tested, in MB.测试内存的大小,以MB为单位
      int64 reserve_mb_;                  // Reserve at least this amount of memory
                                          // for the system, in MB.为系统预留至少多少的内存量,单位是MB。
      int64 min_hugepages_mbytes_;        // Minimum hugepages size.最小的大页面大小
      int64 freepages_;                   // How many invalid pages we need.需要多少无效页面。
      int disk_pages_;                    // Number of pages per temp file.每个临时文件的页数。
      uint64 paddr_base_;                 // Physical address base.物理基地址
      uint64 channel_hash_;               // Mask of address bits XORed for channel.地址位的掩码为通道xor。
      int channel_width_;                 // Channel width in bits.以bit为单位的channel位宽。
      vector< vector > channels_;  // Memory module names per channel.每个channel的内存模块名称。
      // Control flags.
      volatile sig_atomic_t user_break_;  // User has signalled early exit.  Used as
                                          // a boolean.发出提前退出信号。
      int verbosity_;                     // How much to print.打印的数量级
      int print_delay_;                   // Chatty update frequency.打印频率或间隔时间
      int strict_;                        // Check results per transaction.检查每个事务的结果。
      int warm_;                          // FPU warms CPU while copying.
      int address_mode_;                  // 32 or 64 bit binary.地址的bit数
      bool stop_on_error_;                // Exit immendiately on any error.一旦出现错误,立即退出。
      bool findfiles_;                    // Autodetect tempfile locations.自动检测临时文件位置。
      bool error_injection_;              // Simulate errors, for unittests. 模拟错误,用于memory的单元测试。
      bool crazy_error_injection_;        // Simulate lots of errors.模拟大量错误。
      uint64 max_errorcount_;             // Number of errors before forced exit.允许的最大错误数,当错误数达到这个设定值将强制退出程序
      int run_on_anything_;               // Ignore unknown machine ereor.忽略未知机器
      bool use_logfile_;                  // Log to a file.确定要不要向文件中写日志。
      char logfilename_[255];             // Name of file to log to.日志文件的路径
      int logfile_;                       // File handle to log to.日志文件的文件句柄。
      bool log_timestamps_;               // Whether to add timestamps to log lines.是否向日志行添加时间戳。
      // Disk thread options.
      int read_block_size_;               // Size of block to read from disk.要从磁盘读取的块的大小。
      int write_block_size_;              // Size of block to write to disk.写入磁盘的块大小。
      int64 segment_size_;                // Size of segment to split disk into.要将磁盘分割成的段大小。
      int cache_size_;                    // Size of disk cache.磁盘缓存大小。
      int blocks_per_segment_;            // Number of blocks to test per segment.每个段要测试的块数。
      int read_threshold_;                // Maximum time (in us) a read should take
                                          // before warning of a slow read.在发出慢读警告之前的最长读时间(单位为us)。
      int write_threshold_;               // Maximum time (in us) a write should
                                          // take before warning of a slow write.在发出慢写警告之前的最长读时间(单位为us)。
      int non_destructive_;               // Whether to use non-destructive mode for
                                          // the disk test. 无损检测方式进行硬盘测试的标志。
      // Generic Options.
      int monitor_mode_;                  // Switch for monitor-only mode SAT.
                                          // This switch trumps most of the other
                                          // argument, as SAT will only run error
                                          // polling threads.
      int tag_mode_;                      // Do tagging of memory and strict
                                          // checking for misplaced cachelines.
      bool do_page_map_;                  // Should we print a list of used pages?
      unsigned char *page_bitmap_;        // Store bitmap of physical pages seen.
      uint64 page_bitmap_size_;           // Length of physical memory represented.
      // Cpu Cache Coherency Options.
      bool cc_test_;                      // Flag to decide whether to start the
                                          // cache coherency threads.
      int cc_cacheline_count_;            // Number of cache line size structures.
      int cc_cacheline_size_;             // Size of a cache line.
      int cc_inc_count_;                  // Number of times to increment the shared
                                          // cache lines structure members.
      // Cpu Frequency Options.
      bool cpu_freq_test_;                // Flag to decide whether to start the
                                          // cpu frequency thread.
      int cpu_freq_threshold_;            // The MHz threshold which will cause
                                          // the test to fail.
      int cpu_freq_round_;                // Round the computed frequency to this
                                          // value.
      // Thread control.
      int file_threads_;                  // Threads of file IO.
      int net_threads_;                   // Threads of network IO.
      int listen_threads_;                // Threads for network IO to connect.
      int memory_threads_;                // Threads of memcpy.
      int invert_threads_;                // Threads of invert.
      int fill_threads_;                  // Threads of memset.
      int check_threads_;                 // Threads of strcmp.
      int cpu_stress_threads_;            // Threads of CPU stress workload.
      int disk_threads_;                  // Threads of disk test.
      int random_threads_;                // Number of random disk threads.
      int total_threads_;                 // Total threads used.
      bool error_poll_;                   // Poll for system errors.
      // Resources.
      cc_cacheline_data *cc_cacheline_data_;  // The cache line sized datastructure
                                              // used by the ccache threads
                                              // (in worker.h).
      vector filename_;           // Filenames for file IO.
      vector ipaddrs_;            // Addresses for network IO.
      vector diskfilename_;       // Filename for disk IO device.
      // Block table for IO device.
      vector blocktables_;
      bool use_affinity_;                 // Should stressapptest set cpu affinity?
      int32 region_mask_;                 // Bitmask of available NUMA regions.
      int32 region_count_;                // Count of available NUMA regions.
      int32 region_[32];                  // Pagecount per region.
      int region_mode_;                   // What to do with NUMA hints?
      static const int kLocalNuma = 1;    // Target local memory.
      static const int kRemoteNuma = 2;   // Target remote memory.
      // Results.
      int64 errorcount_;                  // Total hardware incidents seen.
      int statuscount_;                   // Total test errors seen.
      // Thread type constants and types
      enum ThreadType { kMemoryType = 0,
        kFileIOType = 1,
        kNetIOType = 2,
        kNetSlaveType = 3,
        kCheckType = 4,
        kInvertType = 5,
        kDiskType = 6,
        kRandomDiskType = 7,
        kCPUType = 8,
        kErrorType = 9,
        kCCType = 10,
        kCPUFreqType = 11,
      };
      // Helper functions.
      virtual void AcquireWorkerLock();
      virtual void ReleaseWorkerLock();
      pthread_mutex_t worker_lock_;  // Lock access to the worker thread structure.
      typedef vector WorkerVector;
      typedef map WorkerMap;
      // Contains all worker threads.
      WorkerMap workers_map_;
      // Delay between power spikes.
      time_t pause_delay_;
      // The duration of each pause (for power spikes).
      time_t pause_duration_;
      // For the workers we pause and resume to create power spikes.
      WorkerStatus power_spike_status_;
      // For the workers we never pause.
      WorkerStatus continuous_status_;
      class OsLayer *os_;                   // Os abstraction: put hacks here.
      class PatternList *patternlist_;      // Access to global data patterns.
      // RunAnalysis methods
      void AnalysisAllStats();              // Summary of all runs.
      void MemoryStats();
      void FileStats();
      void NetStats();
      void CheckStats();
      void InvertStats();
      void DiskStats();
      void QueueStats();
      // Physical page use reporting.
      void AddrMapInit();
      void AddrMapUpdate(struct page_entry *pe);
      void AddrMapPrint();
      // additional memory data from google-specific tests.
      // google专用的内存统计方法和选项。
      virtual void GoogleMemoryStats(float *memcopy_data,
                                     float *memcopy_bandwidth);
      virtual void GoogleOsOptions(std::map *options);
      // Page queues, only one of (valid_+empty_) or (finelock_q_) will be used
      // at a time. A commandline switch controls which queue implementation will
      // be used.
      class PageEntryQueue *valid_;        // Page queue structure, valid pages.
      class PageEntryQueue *empty_;        // Page queue structure, free pages.
      class FineLockPEQueue *finelock_q_;  // Page queue with fine-grain locks
      Sat::PageQueueType pe_q_implementation_;   // Queue implementation switch
      DISALLOW_COPY_AND_ASSIGN(Sat);
    };
    Sat *SatFactory();
    #endif  // STRESSAPPTEST_SAT_H_
    

    类结构图:

    总结

    深入剖析了主函数main.cc。主函数的解析包括各个部分的功能和调用关系,以及参数解析和初始化过程。接着,文章详细分析了SAT压力测试对象的接口和数据结构。通过对SAT类头文件的分析,介绍了SAT类中定义的各种方法和成员变量,以及对应的功能和用途。在这些剖析过程中,读者可以深入了解stressapptest程序的内部结构和实现细节,有助于进一步理解该程序的运行机制和性能压力测试的实现原理。