📄 t_exmap.cpp
字号:
/* * (c) John Berthels 2005 <jjberthels@gmail.com>. See COPYING for license. */#include <Trun.hpp>#include "Exmap.hpp"#include "Pcre.hpp"#include <sstream>#include <list>#include <map>class ExmapTest : public Test{public: bool setup(); bool run(); bool teardown();private: double get_pid_size_from_ps(pid_t pid); static const std::string UTILDIR; static const std::string SA_EXE; static const std::string MI_EXE; static const std::string MI_DAT; static const std::string SA_LIB; static const int NUM_INSTANCES; static const int ARRAY_SIZE; static const int NUM_ARRAYS = 10; std::list<FILE *> _popen_handles; unsigned long get_map_alignment(const Elf::FilePtr &elf); struct array_info { std::string name; int resident_percent; int writable_percent; bool shared; bool initialised; }; int num_initialised_arrays(); static const struct array_info ARRAY_INFO[];};using namespace std;using namespace Exmap;using namespace jutil;using namespace Pcre;const string ExmapTest::UTILDIR = "../tools";const string ExmapTest::SA_EXE = UTILDIR + "/sharedarray";const string ExmapTest::MI_EXE = UTILDIR + "/mapit";const string ExmapTest::MI_DAT = "mapit.dat";const string ExmapTest::SA_LIB = UTILDIR + "/libsharedarray.so";const int ExmapTest::NUM_INSTANCES = 10;const int ExmapTest::ARRAY_SIZE = 100 * Elf::page_size();const struct ExmapTest::array_info ExmapTest::ARRAY_INFO[] = { { "donttouch", 0, 0, false, true }, { "readme", 100, 0, true, true }, { "writeme", 100, 100, false, true }, { "readhalf", 50, 0, true, true }, { "writehalf", 50, 50, false, true }, { "uninit_donttouch", 0, 0, false, false }, { "uninit_readme", 100, 0, true, false }, { "uninit_writeme", 100, 100, false, false }, { "uninit_readhalf", 50, 0, true, false }, { "uninit_writehalf", 50, 50, false, false },};int ExmapTest::num_initialised_arrays(){ int result = 0; for (int i = 0; i < NUM_ARRAYS; ++i) { if (ARRAY_INFO[i].initialised) { result++; } } return result;}bool ExmapTest::run(){ SysInfoPtr sysinfo(new LinuxSysInfo); Snapshot snap(sysinfo); is(snap.num_procs(), 0, "zero procs before load"); ok(snap.load(), "can load snapshot"); ok(snap.num_procs() > 0, "some procs after load"); list<ProcessPtr> allprocs = snap.procs(); ok(!allprocs.empty(), "can get a list of procs"); list<ProcessPtr>::iterator proc_it; list<ProcessPtr> procs; bool failed_to_get_sizes = false; SizesPtr sizes; for (proc_it = allprocs.begin(); proc_it != allprocs.end(); ++proc_it) { string cmdline = (*proc_it)->cmdline(); if (cmdline== SA_EXE) { procs.push_back(*proc_it); } sizes = (*proc_it)->sizes(); if (!sizes) { failed_to_get_sizes = true; } } ok(!failed_to_get_sizes, "can get sizes for every proc"); is((int) procs.size(), NUM_INSTANCES, "can find all our sharedarray procs"); ProcessPtr proc = procs.front(); sizes = proc->sizes(); ok(sizes->val(Sizes::VM) > NUM_ARRAYS * ARRAY_SIZE, "image is big enough"); double ps_size = get_pid_size_from_ps(proc->pid()); is_approx_rel(sizes->val(Sizes::VM), ps_size, 0.001, "exmap info matches ps output"); ok(sizes->val(Sizes::RESIDENT) > 0, "nonzero resident size"); ok(sizes->val(Sizes::EFFECTIVE_RESIDENT) > 0, "nonzero eff resident size"); ok(sizes->val(Sizes::EFFECTIVE_RESIDENT) < sizes->val(Sizes::RESIDENT), "effective is smaller than eff resident"); ok(sizes->val(Sizes::MAPPED) > 0, "nonzero mapped size"); ok(sizes->val(Sizes::EFFECTIVE_MAPPED) > 0, "nonzero eff mapped size"); ok(sizes->val(Sizes::EFFECTIVE_MAPPED) < sizes->val(Sizes::MAPPED), "effective is smaller than eff mapped"); list<FilePtr> files; list<FilePtr>::iterator file_it; list<FilePtr> all_files = snap.files(); ok(all_files.size() > 0, "can find some files"); Regexp re; re.compile(SA_LIB + "$"); failed_to_get_sizes = false; for (file_it = all_files.begin(); file_it != all_files.end(); ++file_it) { string name = (*file_it)->name(); if (re.matches(name)) { files.push_back(*file_it); } sizes = (*file_it)->sizes(); if (!sizes) { failed_to_get_sizes = true; } } ok(!failed_to_get_sizes, "can get sizes for every file"); is((int) files.size(), 1, "file only listed once"); FilePtr file = files.front(); ok(file->is_elf(), "file recognised as elf file"); list<ProcessPtr> procs_per_file = file->procs(); is((int) procs_per_file.size(), NUM_INSTANCES, "right number of procs mapping the file"); for (proc_it = procs_per_file.begin(); proc_it != procs_per_file.end(); ++proc_it) { ok(proc->cmdline() == SA_EXE, "each proc has correct cmdline"); } unsigned long map_alignment = get_map_alignment(file->elf()); for (proc_it = procs.begin(); proc_it != procs.end(); ++proc_it) { sizes = proc->sizes(file); is_approx_rel(sizes->val(Sizes::VM), (double) NUM_ARRAYS * (double) ARRAY_SIZE + map_alignment, 0.01, "Shared lib has correct size in each proc"); } Elf::SectionPtr text = file->elf()->section(".text"); ok(text, "can find text section"); ok(text->size() > 0, "text section has nonzero size"); sizes = procs.front()->sizes(file, text->mem_range()); /// This appears to be compiler and system dependent... /* is_approx_rel(sizes->val(Sizes::RESIDENT), 2.0 * text->size(), 0.001, "lib text is resident and mapped twice (!)"); */ Elf::SectionPtr bss = file->elf()->section(".bss"); ok(bss, "can find bss section"); ok(bss->size() > 0, "bss section has nonzero size"); SizesPtr bss_sizes = procs.front()->sizes(file, bss->mem_range()); ok(bss_sizes, "can get sizes for bss section"); Elf::SectionPtr data = file->elf()->section(".data"); ok(data, "can find data section"); ok(data->size() > 0, "data section has nonzero size"); SizesPtr data_sizes = procs.front()->sizes(file, data->mem_range()); ok(data_sizes, "can get sizes for data section"); is(data->size(), bss->size(), "data and bss sections have same size"); is_approx(data_sizes->val(Sizes::MAPPED), bss_sizes->val(Sizes::MAPPED), Elf::page_size(), "data and bss mapped within page of each other"); is_approx(data_sizes->val(Sizes::RESIDENT), bss_sizes->val(Sizes::RESIDENT), Elf::page_size(), "data and bss resident within page of each other"); double bss_resident_arrays_size = 0; double data_resident_arrays_size = 0; double bss_writable_arrays_size = 0; double data_writable_arrays_size = 0; for (int i = 0; i < NUM_ARRAYS; ++i) { if (ARRAY_INFO[i].initialised) { data_resident_arrays_size += ARRAY_SIZE * ARRAY_INFO[i].resident_percent / 100; data_writable_arrays_size += ARRAY_SIZE * ARRAY_INFO[i].writable_percent / 100; } else { bss_resident_arrays_size += ARRAY_SIZE * ARRAY_INFO[i].resident_percent / 100; bss_writable_arrays_size += ARRAY_SIZE * ARRAY_INFO[i].writable_percent / 100; } } for (proc_it = procs.begin(); proc_it != procs.end(); ++proc_it) { sizes = proc->sizes(file, data->mem_range()); is_approx(sizes->val(Sizes::RESIDENT), data_resident_arrays_size, Elf::page_size(), "resident size for data in proc correct with a page"); is_approx(sizes->val(Sizes::WRITABLE), data_writable_arrays_size, Elf::page_size(), "writable size for data in proc correct with a page"); sizes = proc->sizes(file, bss->mem_range()); is_approx_rel(sizes->val(Sizes::RESIDENT), bss_resident_arrays_size, 0.001, "correct resident size for bss in proc"); is_approx_rel(sizes->val(Sizes::WRITABLE), bss_writable_arrays_size, 0.001, "correct writable size for bss in proc"); } for (int i = 0; i < NUM_ARRAYS; ++i) { const struct array_info *info = ARRAY_INFO + i; string symname = info->name; Elf::SymbolPtr sym = file->elf()->symbol(symname); ok(sym, "can find symbol " + symname); is(sym->size(), ARRAY_SIZE, symname + " has correct size"); sizes = proc->sizes(file, sym->range()); is_approx_rel((int) sizes->val(Sizes::RESIDENT), ARRAY_SIZE * info->resident_percent / 100, 0.001, symname + " has correct resident size"); is_approx_rel(sizes->val(Sizes::WRITABLE), ARRAY_SIZE * info->writable_percent / 100.0, 0.001, symname + " has correct writable size"); // Uninitialised space which is only read appears to be shared // amongst every proc in the system (a 'zero page'?). This is // good from a low-memusage point of view, but it means that // it is shared among nearly all running procs to varying // degrees. So we can't really account for it. if (symname == "uninit_readme" || symname == "uninit_readhalf") { continue; } int expected_eff_resident = ARRAY_SIZE * info->resident_percent / 100; if (info->shared) { expected_eff_resident /= NUM_INSTANCES; } // approximate match since the percentage (fixed pt) // arithmetic could put us off by a factor of 1/100 (and does...:-) is_approx((int) sizes->val(Sizes::EFFECTIVE_RESIDENT), expected_eff_resident, 1 + (expected_eff_resident / 100), symname + " has correct effective size"); } // Non-elf maps procs.clear(); for (proc_it = allprocs.begin(); proc_it != allprocs.end(); ++proc_it) { string cmdline = (*proc_it)->cmdline(); if (cmdline == MI_EXE) { procs.push_back(*proc_it); } } is((int) procs.size(), NUM_INSTANCES, "can find all our mapit procs"); proc = procs.front(); files.clear(); re.compile("/" + MI_DAT + "$"); for (file_it = all_files.begin(); file_it != all_files.end(); ++file_it) { string name = (*file_it)->name(); if (re.matches(name)) { files.push_back(*file_it); } } is((int) files.size(), 1, MI_DAT + " file only listed once"); file = files.front(); ok(!file->is_elf(), MI_DAT + " isn't an elf file"); off_t fsize = 0; double mi_data_size = 0; ok(file_size(MI_DAT, fsize), "can get file size"); mi_data_size = fsize; ok(mi_data_size > 0, "file has nonzero size"); for (proc_it = procs.begin(); proc_it != procs.end(); ++proc_it) { sizes = (*proc_it)->sizes(file); is_approx_rel(sizes->val(Sizes::VM), mi_data_size, 0.001, "correct data file size"); is_approx_rel(sizes->val(Sizes::RESIDENT), mi_data_size, 0.001, "whole data file is resident"); is_approx_rel(sizes->val(Sizes::EFFECTIVE_RESIDENT), mi_data_size / NUM_INSTANCES, 0.001, "data file is shared between instances correctly"); } return true;}bool ExmapTest::setup(){ plan(197); const string ld_path_env = "LD_LIBRARY_PATH"; const char *cp = getenv(ld_path_env.c_str()); string ld_path; if (cp != NULL) { ld_path = cp; } ld_path += ":" + UTILDIR; string putenv_arg = ld_path_env + "=" + ld_path; putenv(const_cast<char *>(putenv_arg.c_str())); for (int i = 0; i < NUM_INSTANCES; ++i) { FILE *fp = popen(SA_EXE.c_str(), "w"); ok(fp, "can start instance of " + SA_EXE); _popen_handles.push_back(fp); fp = popen(MI_EXE.c_str(), "w"); ok(fp, "can start instance of " + MI_EXE); _popen_handles.push_back(fp); } /* let subprocs get ahead. racy. */ sleep(1); return true;}bool ExmapTest::teardown(){ while (!_popen_handles.empty()) { FILE *fp = _popen_handles.front(); if (fp != NULL) { fprintf(fp, "\n"); pclose(fp); } _popen_handles.pop_front(); } return true;}double ExmapTest::get_pid_size_from_ps(pid_t pid){ list<string> lines; list<string> captures; ok(read_proc_output("ps -e -o pid,vsz", lines), "can read proc output"); Regexp re; stringstream sstr; sstr << "\\s*" << pid << "\\s"; re.compile(sstr.str()); re.grep(lines); is((int) lines.size(), 1, "found line for pid"); re.compile("\\s*(\\d+)\\s+(\\d+)$"); re.match_capture(lines.front(), captures); is((int) captures.size(), 2, "Found my captures"); int size = atoi(captures.back().c_str()); return size * 1024.0;}RUN_TEST_CLASS(ExmapTest);unsigned long ExmapTest::get_map_alignment(const Elf::FilePtr &elf){ list<Elf::SegmentPtr> segs = elf->loadable_segments(); list<Elf::SegmentPtr>::iterator it; unsigned long alignment = 0; bool all_aligns_the_same = true; for (it = segs.begin(); it != segs.end(); ++it) { if (alignment == 0) { // Get the alignment alignment = (*it)->align(); } else { // Check they're all the same if (alignment != (*it)->align()) { all_aligns_the_same = false; } } } ok(all_aligns_the_same, "ELF file has all loadable segments with same alignment\n"); return alignment;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -