Home | History | Annotate | Download | only in simpleperf
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <gtest/gtest.h>
     18 
     19 #include <set>
     20 #include <unordered_map>
     21 
     22 #include <android-base/file.h>
     23 #include <android-base/strings.h>
     24 #include <android-base/test_utils.h>
     25 
     26 #include "command.h"
     27 #include "get_test_data.h"
     28 #include "perf_regs.h"
     29 #include "read_apk.h"
     30 #include "test_util.h"
     31 
     32 static std::unique_ptr<Command> ReportCmd() {
     33   return CreateCommandInstance("report");
     34 }
     35 
     36 class ReportCommandTest : public ::testing::Test {
     37  protected:
     38   void Report(
     39       const std::string& perf_data,
     40       const std::vector<std::string>& add_args = std::vector<std::string>()) {
     41     ReportRaw(GetTestData(perf_data), add_args);
     42   }
     43 
     44   void ReportRaw(
     45       const std::string& perf_data,
     46       const std::vector<std::string>& add_args = std::vector<std::string>()) {
     47     success = false;
     48     TemporaryFile tmp_file;
     49     std::vector<std::string> args = {
     50         "-i", perf_data, "--symfs", GetTestDataDir(), "-o", tmp_file.path};
     51     args.insert(args.end(), add_args.begin(), add_args.end());
     52     ASSERT_TRUE(ReportCmd()->Run(args));
     53     ASSERT_TRUE(android::base::ReadFileToString(tmp_file.path, &content));
     54     ASSERT_TRUE(!content.empty());
     55     std::vector<std::string> raw_lines = android::base::Split(content, "\n");
     56     lines.clear();
     57     for (const auto& line : raw_lines) {
     58       std::string s = android::base::Trim(line);
     59       if (!s.empty()) {
     60         lines.push_back(s);
     61       }
     62     }
     63     ASSERT_GE(lines.size(), 2u);
     64     success = true;
     65   }
     66 
     67   std::string content;
     68   std::vector<std::string> lines;
     69   bool success;
     70 };
     71 
     72 TEST_F(ReportCommandTest, no_option) {
     73   Report(PERF_DATA);
     74   ASSERT_TRUE(success);
     75   ASSERT_NE(content.find("GlobalFunc"), std::string::npos);
     76 }
     77 
     78 TEST_F(ReportCommandTest, report_symbol_from_elf_file_with_mini_debug_info) {
     79   Report(PERF_DATA_WITH_MINI_DEBUG_INFO);
     80   ASSERT_TRUE(success);
     81   ASSERT_NE(content.find("GlobalFunc"), std::string::npos);
     82 }
     83 
     84 TEST_F(ReportCommandTest, sort_option_pid) {
     85   Report(PERF_DATA, {"--sort", "pid"});
     86   ASSERT_TRUE(success);
     87   size_t line_index = 0;
     88   while (line_index < lines.size() &&
     89          lines[line_index].find("Pid") == std::string::npos) {
     90     line_index++;
     91   }
     92   ASSERT_LT(line_index + 2, lines.size());
     93 }
     94 
     95 TEST_F(ReportCommandTest, sort_option_more_than_one) {
     96   Report(PERF_DATA, {"--sort", "comm,pid,dso,symbol"});
     97   ASSERT_TRUE(success);
     98   size_t line_index = 0;
     99   while (line_index < lines.size() &&
    100          lines[line_index].find("Overhead") == std::string::npos) {
    101     line_index++;
    102   }
    103   ASSERT_LT(line_index + 1, lines.size());
    104   ASSERT_NE(lines[line_index].find("Command"), std::string::npos);
    105   ASSERT_NE(lines[line_index].find("Pid"), std::string::npos);
    106   ASSERT_NE(lines[line_index].find("Shared Object"), std::string::npos);
    107   ASSERT_NE(lines[line_index].find("Symbol"), std::string::npos);
    108   ASSERT_EQ(lines[line_index].find("Tid"), std::string::npos);
    109 }
    110 
    111 TEST_F(ReportCommandTest, children_option) {
    112   Report(CALLGRAPH_FP_PERF_DATA, {"--children", "--sort", "symbol"});
    113   ASSERT_TRUE(success);
    114   std::unordered_map<std::string, std::pair<double, double>> map;
    115   for (size_t i = 0; i < lines.size(); ++i) {
    116     char name[1024];
    117     std::pair<double, double> pair;
    118     if (sscanf(lines[i].c_str(), "%lf%%%lf%%%s", &pair.first, &pair.second,
    119                name) == 3) {
    120       map.insert(std::make_pair(name, pair));
    121     }
    122   }
    123   ASSERT_NE(map.find("GlobalFunc"), map.end());
    124   ASSERT_NE(map.find("main"), map.end());
    125   auto func_pair = map["GlobalFunc"];
    126   auto main_pair = map["main"];
    127   ASSERT_GE(main_pair.first, func_pair.first);
    128   ASSERT_GE(func_pair.first, func_pair.second);
    129   ASSERT_GE(func_pair.second, main_pair.second);
    130 }
    131 
    132 static bool CheckCalleeMode(std::vector<std::string>& lines) {
    133   bool found = false;
    134   for (size_t i = 0; i + 1 < lines.size(); ++i) {
    135     if (lines[i].find("GlobalFunc") != std::string::npos &&
    136         lines[i + 1].find("main") != std::string::npos) {
    137       found = true;
    138       break;
    139     }
    140   }
    141   return found;
    142 }
    143 
    144 static bool CheckCallerMode(std::vector<std::string>& lines) {
    145   bool found = false;
    146   for (size_t i = 0; i + 1 < lines.size(); ++i) {
    147     if (lines[i].find("main") != std::string::npos &&
    148         lines[i + 1].find("GlobalFunc") != std::string::npos) {
    149       found = true;
    150       break;
    151     }
    152   }
    153   return found;
    154 }
    155 
    156 TEST_F(ReportCommandTest, callgraph_option) {
    157   Report(CALLGRAPH_FP_PERF_DATA, {"-g"});
    158   ASSERT_TRUE(success);
    159   ASSERT_TRUE(CheckCallerMode(lines));
    160   Report(CALLGRAPH_FP_PERF_DATA, {"-g", "callee"});
    161   ASSERT_TRUE(success);
    162   ASSERT_TRUE(CheckCalleeMode(lines));
    163   Report(CALLGRAPH_FP_PERF_DATA, {"-g", "caller"});
    164   ASSERT_TRUE(success);
    165   ASSERT_TRUE(CheckCallerMode(lines));
    166 }
    167 
    168 static bool AllItemsWithString(std::vector<std::string>& lines,
    169                                const std::vector<std::string>& strs) {
    170   size_t line_index = 0;
    171   while (line_index < lines.size() &&
    172          lines[line_index].find("Overhead") == std::string::npos) {
    173     line_index++;
    174   }
    175   if (line_index == lines.size() || line_index + 1 == lines.size()) {
    176     return false;
    177   }
    178   line_index++;
    179   for (; line_index < lines.size(); ++line_index) {
    180     bool exist = false;
    181     for (auto& s : strs) {
    182       if (lines[line_index].find(s) != std::string::npos) {
    183         exist = true;
    184         break;
    185       }
    186     }
    187     if (!exist) {
    188       return false;
    189     }
    190   }
    191   return true;
    192 }
    193 
    194 TEST_F(ReportCommandTest, pid_filter_option) {
    195   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "pid"});
    196   ASSERT_TRUE(success);
    197   ASSERT_FALSE(AllItemsWithString(lines, {"17441"}));
    198   ASSERT_FALSE(AllItemsWithString(lines, {"17441", "17443"}));
    199   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS,
    200          {"--sort", "pid", "--pids", "17441"});
    201   ASSERT_TRUE(success);
    202   ASSERT_TRUE(AllItemsWithString(lines, {"17441"}));
    203   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS,
    204          {"--sort", "pid", "--pids", "17441,17443"});
    205   ASSERT_TRUE(success);
    206   ASSERT_TRUE(AllItemsWithString(lines, {"17441", "17443"}));
    207 
    208   // Test that --pids option is not the same as --tids option.
    209   // Thread 17445 and 17441 are in process 17441.
    210   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS,
    211          {"--sort", "tid", "--pids", "17441"});
    212   ASSERT_TRUE(success);
    213   ASSERT_NE(content.find("17441"), std::string::npos);
    214   ASSERT_NE(content.find("17445"), std::string::npos);
    215 }
    216 
    217 TEST_F(ReportCommandTest, wrong_pid_filter_option) {
    218   ASSERT_EXIT(
    219       {
    220         Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--pids", "2,bogus"});
    221         exit(success ? 0 : 1);
    222       },
    223       testing::ExitedWithCode(1), "invalid id in --pids option: bogus");
    224 }
    225 
    226 TEST_F(ReportCommandTest, tid_filter_option) {
    227   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--sort", "tid"});
    228   ASSERT_TRUE(success);
    229   ASSERT_FALSE(AllItemsWithString(lines, {"17441"}));
    230   ASSERT_FALSE(AllItemsWithString(lines, {"17441", "17445"}));
    231   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS,
    232          {"--sort", "tid", "--tids", "17441"});
    233   ASSERT_TRUE(success);
    234   ASSERT_TRUE(AllItemsWithString(lines, {"17441"}));
    235   Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS,
    236          {"--sort", "tid", "--tids", "17441,17445"});
    237   ASSERT_TRUE(success);
    238   ASSERT_TRUE(AllItemsWithString(lines, {"17441", "17445"}));
    239 }
    240 
    241 TEST_F(ReportCommandTest, wrong_tid_filter_option) {
    242   ASSERT_EXIT(
    243       {
    244         Report(PERF_DATA_WITH_MULTIPLE_PIDS_AND_TIDS, {"--tids", "2,bogus"});
    245         exit(success ? 0 : 1);
    246       },
    247       testing::ExitedWithCode(1), "invalid id in --tids option: bogus");
    248 }
    249 
    250 TEST_F(ReportCommandTest, comm_filter_option) {
    251   Report(PERF_DATA, {"--sort", "comm"});
    252   ASSERT_TRUE(success);
    253   ASSERT_FALSE(AllItemsWithString(lines, {"t1"}));
    254   ASSERT_FALSE(AllItemsWithString(lines, {"t1", "t2"}));
    255   Report(PERF_DATA, {"--sort", "comm", "--comms", "t1"});
    256   ASSERT_TRUE(success);
    257   ASSERT_TRUE(AllItemsWithString(lines, {"t1"}));
    258   Report(PERF_DATA, {"--sort", "comm", "--comms", "t1,t2"});
    259   ASSERT_TRUE(success);
    260   ASSERT_TRUE(AllItemsWithString(lines, {"t1", "t2"}));
    261 }
    262 
    263 TEST_F(ReportCommandTest, dso_filter_option) {
    264   Report(PERF_DATA, {"--sort", "dso"});
    265   ASSERT_TRUE(success);
    266   ASSERT_FALSE(AllItemsWithString(lines, {"/t1"}));
    267   ASSERT_FALSE(AllItemsWithString(lines, {"/t1", "/t2"}));
    268   Report(PERF_DATA, {"--sort", "dso", "--dsos", "/t1"});
    269   ASSERT_TRUE(success);
    270   ASSERT_TRUE(AllItemsWithString(lines, {"/t1"}));
    271   Report(PERF_DATA, {"--sort", "dso", "--dsos", "/t1,/t2"});
    272   ASSERT_TRUE(success);
    273   ASSERT_TRUE(AllItemsWithString(lines, {"/t1", "/t2"}));
    274 }
    275 
    276 TEST_F(ReportCommandTest, symbol_filter_option) {
    277   Report(PERF_DATA_WITH_SYMBOLS, {"--sort", "symbol"});
    278   ASSERT_TRUE(success);
    279   ASSERT_FALSE(AllItemsWithString(lines, {"func2(int, int)"}));
    280   ASSERT_FALSE(AllItemsWithString(lines, {"main", "func2(int, int)"}));
    281   Report(PERF_DATA_WITH_SYMBOLS,
    282          {"--sort", "symbol", "--symbols", "func2(int, int)"});
    283   ASSERT_TRUE(success);
    284   ASSERT_TRUE(AllItemsWithString(lines, {"func2(int, int)"}));
    285   Report(PERF_DATA_WITH_SYMBOLS,
    286          {"--sort", "symbol", "--symbols", "main;func2(int, int)"});
    287   ASSERT_TRUE(success);
    288   ASSERT_TRUE(AllItemsWithString(lines, {"main", "func2(int, int)"}));
    289 }
    290 
    291 TEST_F(ReportCommandTest, use_branch_address) {
    292   Report(BRANCH_PERF_DATA, {"-b", "--sort", "symbol_from,symbol_to"});
    293   std::set<std::pair<std::string, std::string>> hit_set;
    294   bool after_overhead = false;
    295   for (const auto& line : lines) {
    296     if (!after_overhead && line.find("Overhead") != std::string::npos) {
    297       after_overhead = true;
    298     } else if (after_overhead) {
    299       char from[80];
    300       char to[80];
    301       if (sscanf(line.c_str(), "%*f%%%s%s", from, to) == 2) {
    302         hit_set.insert(std::make_pair<std::string, std::string>(from, to));
    303       }
    304     }
    305   }
    306   ASSERT_NE(hit_set.find(std::make_pair<std::string, std::string>(
    307                 "GlobalFunc", "CalledFunc")),
    308             hit_set.end());
    309   ASSERT_NE(hit_set.find(std::make_pair<std::string, std::string>(
    310                 "CalledFunc", "GlobalFunc")),
    311             hit_set.end());
    312 }
    313 
    314 TEST_F(ReportCommandTest, report_symbols_of_nativelib_in_apk) {
    315   Report(NATIVELIB_IN_APK_PERF_DATA);
    316   ASSERT_TRUE(success);
    317   ASSERT_NE(content.find(GetUrlInApk(APK_FILE, NATIVELIB_IN_APK)),
    318             std::string::npos);
    319   ASSERT_NE(content.find("Func2"), std::string::npos);
    320 }
    321 
    322 TEST_F(ReportCommandTest, report_more_than_one_event_types) {
    323   Report(PERF_DATA_WITH_TWO_EVENT_TYPES);
    324   ASSERT_TRUE(success);
    325   size_t pos = 0;
    326   ASSERT_NE(pos = content.find("cpu-cycles", pos), std::string::npos);
    327   ASSERT_NE(pos = content.find("Samples:", pos), std::string::npos);
    328   ASSERT_NE(pos = content.find("cpu-clock", pos), std::string::npos);
    329   ASSERT_NE(pos = content.find("Samples:", pos), std::string::npos);
    330 }
    331 
    332 TEST_F(ReportCommandTest, report_kernel_symbol) {
    333   Report(PERF_DATA_WITH_KERNEL_SYMBOL);
    334   ASSERT_TRUE(success);
    335   ASSERT_NE(content.find("perf_event_aux"), std::string::npos);
    336 }
    337 
    338 TEST_F(ReportCommandTest, report_dumped_symbols) {
    339   Report(PERF_DATA_WITH_SYMBOLS);
    340   ASSERT_TRUE(success);
    341   ASSERT_NE(content.find("main"), std::string::npos);
    342   Report(PERF_DATA_WITH_SYMBOLS_FOR_NONZERO_MINVADDR_DSO);
    343   ASSERT_TRUE(success);
    344   ASSERT_NE(content.find("memcpy"), std::string::npos);
    345 }
    346 
    347 TEST_F(ReportCommandTest, report_dumped_symbols_with_symfs_dir) {
    348   // Check if we can report symbols when they appear both in perf.data and symfs dir.
    349   Report(PERF_DATA_WITH_SYMBOLS, {"--symfs", GetTestDataDir()});
    350   ASSERT_TRUE(success);
    351   ASSERT_NE(content.find("main"), std::string::npos);
    352 }
    353 
    354 TEST_F(ReportCommandTest, report_sort_vaddr_in_file) {
    355   Report(PERF_DATA, {"--sort", "vaddr_in_file"});
    356   ASSERT_TRUE(success);
    357   ASSERT_NE(content.find("VaddrInFile"), std::string::npos);
    358 }
    359 
    360 TEST_F(ReportCommandTest, check_build_id) {
    361   Report(PERF_DATA_FOR_BUILD_ID_CHECK,
    362          {"--symfs", GetTestData(CORRECT_SYMFS_FOR_BUILD_ID_CHECK)});
    363   ASSERT_TRUE(success);
    364   ASSERT_NE(content.find("main"), std::string::npos);
    365   ASSERT_EXIT(
    366       {
    367         Report(PERF_DATA_FOR_BUILD_ID_CHECK,
    368                {"--symfs", GetTestData(WRONG_SYMFS_FOR_BUILD_ID_CHECK)});
    369         if (!success) {
    370           exit(1);
    371         }
    372         if (content.find("main") != std::string::npos) {
    373           exit(2);
    374         }
    375         exit(0);
    376       },
    377       testing::ExitedWithCode(0), "Build id mismatch");
    378 }
    379 
    380 TEST_F(ReportCommandTest, no_show_ip_option) {
    381   Report(PERF_DATA);
    382   ASSERT_TRUE(success);
    383   ASSERT_EQ(content.find("unknown"), std::string::npos);
    384   Report(PERF_DATA, {"--no-show-ip"});
    385   ASSERT_TRUE(success);
    386   ASSERT_NE(content.find("unknown"), std::string::npos);
    387 }
    388 
    389 TEST_F(ReportCommandTest, no_symbol_table_warning) {
    390   ASSERT_EXIT(
    391       {
    392         Report(PERF_DATA,
    393                {"--symfs", GetTestData(SYMFS_FOR_NO_SYMBOL_TABLE_WARNING)});
    394         if (!success) {
    395           exit(1);
    396         }
    397         if (content.find("GlobalFunc") != std::string::npos) {
    398           exit(2);
    399         }
    400         exit(0);
    401       },
    402       testing::ExitedWithCode(0), "elf doesn't contain symbol table");
    403 }
    404 
    405 TEST_F(ReportCommandTest, read_elf_file_warning) {
    406   ASSERT_EXIT(
    407       {
    408         Report(PERF_DATA,
    409                {"--symfs", GetTestData(SYMFS_FOR_READ_ELF_FILE_WARNING)});
    410         if (!success) {
    411           exit(1);
    412         }
    413         if (content.find("GlobalFunc") != std::string::npos) {
    414           exit(2);
    415         }
    416         exit(0);
    417       },
    418       testing::ExitedWithCode(0), "elf: Read failed");
    419 }
    420 
    421 TEST_F(ReportCommandTest, report_data_generated_by_linux_perf) {
    422   Report(PERF_DATA_GENERATED_BY_LINUX_PERF);
    423   ASSERT_TRUE(success);
    424 }
    425 
    426 TEST_F(ReportCommandTest, max_stack_and_percent_limit_option) {
    427   Report(PERF_DATA_MAX_STACK_AND_PERCENT_LIMIT, {"-g"});
    428   ASSERT_TRUE(success);
    429   ASSERT_NE(content.find("89.03"), std::string::npos);
    430 
    431   Report(PERF_DATA_MAX_STACK_AND_PERCENT_LIMIT, {"-g", "--max-stack", "0"});
    432   ASSERT_TRUE(success);
    433   ASSERT_EQ(content.find("89.03"), std::string::npos);
    434   Report(PERF_DATA_MAX_STACK_AND_PERCENT_LIMIT, {"-g", "--max-stack", "2"});
    435   ASSERT_TRUE(success);
    436   ASSERT_NE(content.find("89.03"), std::string::npos);
    437 
    438   Report(PERF_DATA_MAX_STACK_AND_PERCENT_LIMIT,
    439          {"-g", "--percent-limit", "90"});
    440   ASSERT_TRUE(success);
    441   ASSERT_EQ(content.find("89.03"), std::string::npos);
    442   Report(PERF_DATA_MAX_STACK_AND_PERCENT_LIMIT,
    443          {"-g", "--percent-limit", "70"});
    444   ASSERT_TRUE(success);
    445   ASSERT_NE(content.find("89.03"), std::string::npos);
    446 }
    447 
    448 TEST_F(ReportCommandTest, kallsyms_option) {
    449   Report(PERF_DATA, {"--kallsyms", GetTestData("kallsyms")});
    450   ASSERT_TRUE(success);
    451   ASSERT_NE(content.find("FakeKernelSymbol"), std::string::npos);
    452 }
    453 
    454 TEST_F(ReportCommandTest, invalid_perf_data) {
    455   ASSERT_FALSE(ReportCmd()->Run({"-i", GetTestData(INVALID_PERF_DATA)}));
    456 }
    457 
    458 TEST_F(ReportCommandTest, raw_period_option) {
    459   Report(PERF_DATA, {"--raw-period"});
    460   ASSERT_TRUE(success);
    461   ASSERT_NE(content.find("GlobalFunc"), std::string::npos);
    462   ASSERT_EQ(content.find('%'), std::string::npos);
    463 }
    464 
    465 TEST_F(ReportCommandTest, full_callgraph_option) {
    466   Report(CALLGRAPH_FP_PERF_DATA, {"-g"});
    467   ASSERT_TRUE(success);
    468   ASSERT_NE(content.find("skipped in brief callgraph mode"), std::string::npos);
    469   Report(CALLGRAPH_FP_PERF_DATA, {"-g", "--full-callgraph"});
    470   ASSERT_TRUE(success);
    471   ASSERT_EQ(content.find("skipped in brief callgraph mode"), std::string::npos);
    472 }
    473 
    474 TEST_F(ReportCommandTest, report_offcpu_time) {
    475   Report(PERF_DATA_WITH_TRACE_OFFCPU, {"--children"});
    476   ASSERT_TRUE(success);
    477   ASSERT_NE(content.find("Time in ns"), std::string::npos);
    478   bool found = false;
    479   for (auto& line : lines) {
    480     if (line.find("SleepFunction") != std::string::npos) {
    481       ASSERT_NE(line.find("38.77%"), std::string::npos);
    482       found = true;
    483       break;
    484     }
    485   }
    486   ASSERT_TRUE(found);
    487 }
    488 
    489 #if defined(__linux__)
    490 #include "event_selection_set.h"
    491 
    492 static std::unique_ptr<Command> RecordCmd() {
    493   return CreateCommandInstance("record");
    494 }
    495 
    496 TEST_F(ReportCommandTest, dwarf_callgraph) {
    497   OMIT_TEST_ON_NON_NATIVE_ABIS();
    498   ASSERT_TRUE(IsDwarfCallChainSamplingSupported());
    499   std::vector<std::unique_ptr<Workload>> workloads;
    500   CreateProcesses(1, &workloads);
    501   std::string pid = std::to_string(workloads[0]->GetPid());
    502   TemporaryFile tmp_file;
    503   ASSERT_TRUE(
    504       RecordCmd()->Run({"-p", pid, "-g", "-o", tmp_file.path, "sleep", SLEEP_SEC}));
    505   ReportRaw(tmp_file.path, {"-g"});
    506   ASSERT_TRUE(success);
    507 }
    508 
    509 TEST_F(ReportCommandTest, report_dwarf_callgraph_of_nativelib_in_apk) {
    510   Report(NATIVELIB_IN_APK_PERF_DATA, {"-g"});
    511   ASSERT_NE(content.find(GetUrlInApk(APK_FILE, NATIVELIB_IN_APK)),
    512             std::string::npos);
    513   ASSERT_NE(content.find("Func2"), std::string::npos);
    514   ASSERT_NE(content.find("Func1"), std::string::npos);
    515   ASSERT_NE(content.find("GlobalFunc"), std::string::npos);
    516 }
    517 
    518 TEST_F(ReportCommandTest, exclude_kernel_callchain) {
    519   TEST_REQUIRE_HOST_ROOT();
    520   OMIT_TEST_ON_NON_NATIVE_ABIS();
    521   std::vector<std::unique_ptr<Workload>> workloads;
    522   CreateProcesses(1, &workloads);
    523   std::string pid = std::to_string(workloads[0]->GetPid());
    524   TemporaryFile tmpfile;
    525   ASSERT_TRUE(RecordCmd()->Run({"--trace-offcpu", "-e", "cpu-cycles:u", "-p", pid,
    526                                 "--duration", "2", "-o", tmpfile.path, "-g"}));
    527   ReportRaw(tmpfile.path, {"-g"});
    528   ASSERT_TRUE(success);
    529   ASSERT_EQ(content.find("[kernel.kallsyms]"), std::string::npos);
    530 }
    531 
    532 #endif
    533