#include <gtest/gtest.h>
+#include <unistd.h>
+
+#include <android-base/file.h>
#include <android-base/stringprintf.h>
#include <android-base/test_utils.h>
#include <map>
#include <memory>
+#include <thread>
#include "command.h"
#include "environment.h"
#include "record.h"
#include "record_file.h"
#include "test_util.h"
+#include "thread_tree.h"
using namespace PerfFileFormat;
if (record->type() == PERF_RECORD_MMAP) {
const MmapRecord* mmap_record =
static_cast<const MmapRecord*>(record.get());
- if (strcmp(mmap_record->filename, DEFAULT_KERNEL_MMAP_NAME) == 0) {
+ if (strcmp(mmap_record->filename, DEFAULT_KERNEL_MMAP_NAME) == 0 ||
+ strcmp(mmap_record->filename, DEFAULT_KERNEL_MMAP_NAME_PERF) == 0) {
have_kernel_mmap = true;
break;
}
ASSERT_TRUE(RunRecordCmd({"--call-graph", "fp"}));
}
+TEST(record_cmd, fp_callchain_sampling_warning_on_arm) {
+ if (GetBuildArch() != ARCH_ARM) {
+ GTEST_LOG_(INFO) << "This test does nothing as it only tests on arm arch.";
+ return;
+ }
+ ASSERT_EXIT(
+ {
+ exit(RunRecordCmd({"--call-graph", "fp"}) ? 0 : 1);
+ },
+ testing::ExitedWithCode(0), "doesn't work well on arm");
+}
+
TEST(record_cmd, system_wide_fp_callchain_sampling) {
TEST_IN_ROOT(ASSERT_TRUE(RunRecordCmd({"-a", "--call-graph", "fp"})));
}
TEST(record_cmd, dwarf_callchain_sampling) {
if (IsDwarfCallChainSamplingSupported()) {
- ASSERT_TRUE(RunRecordCmd({"--call-graph", "dwarf"}));
- ASSERT_TRUE(RunRecordCmd({"--call-graph", "dwarf,16384"}));
- ASSERT_FALSE(RunRecordCmd({"--call-graph", "dwarf,65536"}));
- ASSERT_TRUE(RunRecordCmd({"-g"}));
+ std::vector<std::unique_ptr<Workload>> workloads;
+ CreateProcesses(1, &workloads);
+ std::string pid = std::to_string(workloads[0]->GetPid());
+ ASSERT_TRUE(RunRecordCmd({"-p", pid, "--call-graph", "dwarf"}));
+ ASSERT_TRUE(RunRecordCmd({"-p", pid, "--call-graph", "dwarf,16384"}));
+ ASSERT_FALSE(RunRecordCmd({"-p", pid, "--call-graph", "dwarf,65536"}));
+ ASSERT_TRUE(RunRecordCmd({"-p", pid, "-g"}));
} else {
GTEST_LOG_(INFO) << "This test does nothing as dwarf callchain sampling is "
"not supported on this device.";
TEST(record_cmd, post_unwind_option) {
if (IsDwarfCallChainSamplingSupported()) {
- ASSERT_TRUE(RunRecordCmd({"--call-graph", "dwarf", "--post-unwind"}));
+ std::vector<std::unique_ptr<Workload>> workloads;
+ CreateProcesses(1, &workloads);
+ std::string pid = std::to_string(workloads[0]->GetPid());
+ ASSERT_TRUE(RunRecordCmd({"-p", pid, "--call-graph", "dwarf", "--post-unwind"}));
} else {
GTEST_LOG_(INFO) << "This test does nothing as dwarf callchain sampling is "
"not supported on this device.";
has_kernel_symbol_records = true;
}
}
- ASSERT_EQ(need_kallsyms, has_kernel_symbol_records);
+ bool require_kallsyms = need_kallsyms && CheckKernelSymbolAddresses();
+ ASSERT_EQ(require_kallsyms, has_kernel_symbol_records);
*success = true;
}
TEST(record_cmd, kernel_symbol) {
TemporaryFile tmpfile;
- ASSERT_TRUE(RunRecordCmd({}, tmpfile.path));
+ ASSERT_TRUE(RunRecordCmd({"--no-dump-symbols"}, tmpfile.path));
bool success;
CheckKernelSymbol(tmpfile.path, true, &success);
ASSERT_TRUE(success);
- ASSERT_TRUE(RunRecordCmd({"--no-dump-kernel-symbols"}, tmpfile.path));
+ ASSERT_TRUE(RunRecordCmd({"--no-dump-symbols", "--no-dump-kernel-symbols"}, tmpfile.path));
CheckKernelSymbol(tmpfile.path, false, &success);
ASSERT_TRUE(success);
}
*success = true;
}
-TEST(record_cmd, dump_symbols) {
+TEST(record_cmd, no_dump_symbols) {
TemporaryFile tmpfile;
ASSERT_TRUE(RunRecordCmd({}, tmpfile.path));
bool success;
- CheckDsoSymbolRecords(tmpfile.path, false, &success);
- ASSERT_TRUE(success);
- ASSERT_TRUE(RunRecordCmd({"--dump-symbols"}, tmpfile.path));
CheckDsoSymbolRecords(tmpfile.path, true, &success);
ASSERT_TRUE(success);
+ ASSERT_TRUE(RunRecordCmd({"--no-dump-symbols"}, tmpfile.path));
+ CheckDsoSymbolRecords(tmpfile.path, false, &success);
+ ASSERT_TRUE(success);
if (IsDwarfCallChainSamplingSupported()) {
- ASSERT_TRUE(RunRecordCmd({"-g"}, tmpfile.path));
+ std::vector<std::unique_ptr<Workload>> workloads;
+ CreateProcesses(1, &workloads);
+ std::string pid = std::to_string(workloads[0]->GetPid());
+ ASSERT_TRUE(RunRecordCmd({"-p", pid, "-g"}, tmpfile.path));
bool success;
- CheckDsoSymbolRecords(tmpfile.path, false, &success);
- ASSERT_TRUE(success);
- ASSERT_TRUE(RunRecordCmd({"-g", "--dump-symbols"}, tmpfile.path));
CheckDsoSymbolRecords(tmpfile.path, true, &success);
ASSERT_TRUE(success);
+ ASSERT_TRUE(RunRecordCmd({"-p", pid, "-g", "--no-dump-symbols"}, tmpfile.path));
+ CheckDsoSymbolRecords(tmpfile.path, false, &success);
+ ASSERT_TRUE(success);
}
}
+TEST(record_cmd, dump_kernel_symbols) {
+ if (!IsRoot()) {
+ GTEST_LOG_(INFO) << "Test requires root privilege";
+ return;
+ }
+ TemporaryFile tmpfile;
+ ASSERT_TRUE(RunRecordCmd({"-a", "-o", tmpfile.path, "sleep", "1"}));
+ std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile.path);
+ ASSERT_TRUE(reader != nullptr);
+ std::map<int, SectionDesc> section_map = reader->FeatureSectionDescriptors();
+ ASSERT_NE(section_map.find(FEAT_FILE), section_map.end());
+ std::string file_path;
+ uint32_t file_type;
+ uint64_t min_vaddr;
+ std::vector<Symbol> symbols;
+ size_t read_pos = 0;
+ bool has_kernel_symbols = false;
+ while (reader->ReadFileFeature(read_pos, &file_path, &file_type, &min_vaddr, &symbols)) {
+ if (file_type == DSO_KERNEL && !symbols.empty()) {
+ has_kernel_symbols = true;
+ }
+ }
+ ASSERT_TRUE(has_kernel_symbols);
+}
+
TEST(record_cmd, group_option) {
ASSERT_TRUE(RunRecordCmd({"--group", "cpu-cycles,cpu-clock", "-m", "16"}));
ASSERT_TRUE(RunRecordCmd({"--group", "cpu-cycles,cpu-clock", "--group",
}
}
}
+
+TEST(record_cmd, handle_SIGHUP) {
+ TemporaryFile tmpfile;
+ std::thread thread([]() {
+ sleep(1);
+ kill(getpid(), SIGHUP);
+ });
+ thread.detach();
+ ASSERT_TRUE(RecordCmd()->Run({"-o", tmpfile.path, "sleep", "1000000"}));
+}
+
+TEST(record_cmd, stop_when_no_more_targets) {
+ TemporaryFile tmpfile;
+ std::atomic<int> tid(0);
+ std::thread thread([&]() {
+ tid = gettid();
+ sleep(1);
+ });
+ thread.detach();
+ while (tid == 0);
+ ASSERT_TRUE(RecordCmd()->Run({"-o", tmpfile.path, "-t", std::to_string(tid)}));
+}
+
+TEST(record_cmd, donot_stop_when_having_targets) {
+ std::vector<std::unique_ptr<Workload>> workloads;
+ CreateProcesses(1, &workloads);
+ std::string pid = std::to_string(workloads[0]->GetPid());
+ uint64_t start_time_in_ns = GetSystemClock();
+ TemporaryFile tmpfile;
+ ASSERT_TRUE(RecordCmd()->Run({"-o", tmpfile.path, "-p", pid, "--duration", "3"}));
+ uint64_t end_time_in_ns = GetSystemClock();
+ ASSERT_GT(end_time_in_ns - start_time_in_ns, static_cast<uint64_t>(2e9));
+}
+
+TEST(record_cmd, start_profiling_fd_option) {
+ int pipefd[2];
+ ASSERT_EQ(0, pipe(pipefd));
+ int read_fd = pipefd[0];
+ int write_fd = pipefd[1];
+ ASSERT_EXIT(
+ {
+ close(read_fd);
+ exit(RunRecordCmd({"--start_profiling_fd", std::to_string(write_fd)}) ? 0 : 1);
+ },
+ testing::ExitedWithCode(0), "");
+ close(write_fd);
+ std::string s;
+ ASSERT_TRUE(android::base::ReadFdToString(read_fd, &s));
+ close(read_fd);
+ ASSERT_EQ("STARTED", s);
+}
+
+TEST(record_cmd, record_meta_info_feature) {
+ TemporaryFile tmpfile;
+ ASSERT_TRUE(RunRecordCmd({}, tmpfile.path));
+ std::unique_ptr<RecordFileReader> reader = RecordFileReader::CreateInstance(tmpfile.path);
+ ASSERT_TRUE(reader != nullptr);
+ std::unordered_map<std::string, std::string> info_map;
+ ASSERT_TRUE(reader->ReadMetaInfoFeature(&info_map));
+ ASSERT_NE(info_map.find("simpleperf_version"), info_map.end());
+}