From 5f43fc4ac870b49542b4cf530a3729f9f1e0e9ab Mon Sep 17 00:00:00 2001 From: Yabin Cui Date: Tue, 13 Dec 2016 13:47:49 -0800 Subject: simpleperf: check monitored targets regularly. When monitoring some threads/processes not forked by simpleperf, check if these threads/processes exist regularly. So we can stop profiling once all threads/processes exit. Also handle SIGHUP signal, so we can finish profiling properly when `adb shell simpleperf record xxx` is killed by Ctrl-C. Add corresponding tests. Bug: http://b/33558210 Test: run simpleperf_unit_test. Change-Id: Ieae4d00d099dc1c7a0c51b0610dff43981bb642e --- simpleperf/cmd_record.cpp | 15 +++++++++++---- simpleperf/cmd_record_test.cpp | 24 ++++++++++++++++++++++++ simpleperf/cmd_stat.cpp | 11 +++++++++-- simpleperf/cmd_stat_test.cpp | 23 +++++++++++++++++++++++ simpleperf/environment.cpp | 4 ++++ simpleperf/environment.h | 1 + simpleperf/event_selection_set.cpp | 34 +++++++++++++++++++++++++--------- simpleperf/event_selection_set.h | 20 ++++++++++++++------ 8 files changed, 111 insertions(+), 21 deletions(-) (limited to 'simpleperf') diff --git a/simpleperf/cmd_record.cpp b/simpleperf/cmd_record.cpp index 2d1e0123..eb968d71 100644 --- a/simpleperf/cmd_record.cpp +++ b/simpleperf/cmd_record.cpp @@ -164,7 +164,7 @@ class RecordCommand : public Command { start_sampling_time_in_ns_(0), sample_record_count_(0), lost_record_count_(0) { - // Die if parent exits. + // Stop profiling if parent exits. prctl(PR_SET_PDEATHSIG, SIGHUP, 0, 0, 0); } @@ -252,6 +252,7 @@ bool RecordCommand::Run(const std::vector& args) { return false; } } + bool need_to_check_targets = false; if (system_wide_collection_) { event_selection_set_.AddMonitoredThreads({-1}); } else if (!event_selection_set_.HasMonitoredTarget()) { @@ -263,6 +264,8 @@ bool RecordCommand::Run(const std::vector& args) { << "No threads to monitor. Try `simpleperf help record` for help"; return false; } + } else { + need_to_check_targets = true; } // 3. Open perf_event_files, create mapped buffers for perf_event_files. @@ -281,15 +284,19 @@ bool RecordCommand::Run(const std::vector& args) { // 5. Create IOEventLoop and add read/signal/periodic Events. IOEventLoop loop; + event_selection_set_.SetIOEventLoop(loop); auto callback = std::bind(&RecordCommand::ProcessRecord, this, std::placeholders::_1); - if (!event_selection_set_.PrepareToReadMmapEventData(loop, callback)) { + if (!event_selection_set_.PrepareToReadMmapEventData(callback)) { + return false; + } + if (!event_selection_set_.HandleCpuHotplugEvents(cpus_)) { return false; } - if (!event_selection_set_.HandleCpuHotplugEvents(loop, cpus_)) { + if (need_to_check_targets && !event_selection_set_.StopWhenNoMoreTargets()) { return false; } - if (!loop.AddSignalEvents({SIGCHLD, SIGINT, SIGTERM}, + if (!loop.AddSignalEvents({SIGCHLD, SIGINT, SIGTERM, SIGHUP}, [&]() { return loop.ExitLoop(); })) { return false; } diff --git a/simpleperf/cmd_record_test.cpp b/simpleperf/cmd_record_test.cpp index 35f330ef..28e2e9b1 100644 --- a/simpleperf/cmd_record_test.cpp +++ b/simpleperf/cmd_record_test.cpp @@ -18,9 +18,11 @@ #include #include +#include #include #include +#include #include "command.h" #include "environment.h" @@ -335,3 +337,25 @@ TEST(record_cmd, support_modifier_for_clock_events) { } } } + +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 tid(0); + std::thread thread([&]() { + tid = syscall(__NR_gettid); + sleep(1); + }); + thread.detach(); + while (tid == 0); + ASSERT_TRUE(RecordCmd()->Run({"-o", tmpfile.path, "-t", std::to_string(tid)})); +} diff --git a/simpleperf/cmd_stat.cpp b/simpleperf/cmd_stat.cpp index 06258f36..423fbffb 100644 --- a/simpleperf/cmd_stat.cpp +++ b/simpleperf/cmd_stat.cpp @@ -349,6 +349,7 @@ bool StatCommand::Run(const std::vector& args) { return false; } } + bool need_to_check_targets = false; if (system_wide_collection_) { event_selection_set_.AddMonitoredThreads({-1}); } else if (!event_selection_set_.HasMonitoredTarget()) { @@ -360,6 +361,8 @@ bool StatCommand::Run(const std::vector& args) { << "No threads to monitor. Try `simpleperf help stat` for help\n"; return false; } + } else { + need_to_check_targets = true; } // 3. Open perf_event_files and output file if defined. @@ -382,14 +385,18 @@ bool StatCommand::Run(const std::vector& args) { // 4. Create IOEventLoop and add signal/periodic Events. IOEventLoop loop; + event_selection_set_.SetIOEventLoop(loop); std::chrono::time_point start_time; std::vector counters; if (system_wide_collection_ || (!cpus_.empty() && cpus_[0] != -1)) { - if (!event_selection_set_.HandleCpuHotplugEvents(loop, cpus_)) { + if (!event_selection_set_.HandleCpuHotplugEvents(cpus_)) { return false; } } - if (!loop.AddSignalEvents({SIGCHLD, SIGINT, SIGTERM}, + if (need_to_check_targets && !event_selection_set_.StopWhenNoMoreTargets()) { + return false; + } + if (!loop.AddSignalEvents({SIGCHLD, SIGINT, SIGTERM, SIGHUP}, [&]() { return loop.ExitLoop(); })) { return false; } diff --git a/simpleperf/cmd_stat_test.cpp b/simpleperf/cmd_stat_test.cpp index 125f9386..5aa1e3b5 100644 --- a/simpleperf/cmd_stat_test.cpp +++ b/simpleperf/cmd_stat_test.cpp @@ -19,6 +19,9 @@ #include #include #include +#include + +#include #include "command.h" #include "get_test_data.h" @@ -143,3 +146,23 @@ TEST(stat_cmd, no_modifier_for_clock_events) { } } } + +TEST(stat_cmd, handle_SIGHUP) { + std::thread thread([]() { + sleep(1); + kill(getpid(), SIGHUP); + }); + thread.detach(); + ASSERT_TRUE(StatCmd()->Run({"sleep", "1000000"})); +} + +TEST(stat_cmd, stop_when_no_more_targets) { + std::atomic tid(0); + std::thread thread([&]() { + tid = syscall(__NR_gettid); + sleep(1); + }); + thread.detach(); + while (tid == 0); + ASSERT_TRUE(StatCmd()->Run({"-t", std::to_string(tid)})); +} diff --git a/simpleperf/environment.cpp b/simpleperf/environment.cpp index 3859de18..69f7c144 100644 --- a/simpleperf/environment.cpp +++ b/simpleperf/environment.cpp @@ -238,6 +238,10 @@ std::vector GetThreadsInProcess(pid_t pid) { return result; } +bool IsThreadAlive(pid_t tid) { + return IsDir(android::base::StringPrintf("/proc/%d", tid)); +} + bool GetProcessForThread(pid_t tid, pid_t* pid) { return ReadThreadNameAndPid(tid, nullptr, pid); } diff --git a/simpleperf/environment.h b/simpleperf/environment.h index 3baaf752..5d9cee88 100644 --- a/simpleperf/environment.h +++ b/simpleperf/environment.h @@ -55,6 +55,7 @@ constexpr char DEFAULT_KERNEL_FILENAME_FOR_BUILD_ID[] = "[kernel.kallsyms]"; bool GetKernelBuildId(BuildId* build_id); bool GetModuleBuildId(const std::string& module_name, BuildId* build_id); +bool IsThreadAlive(pid_t tid); std::vector GetAllProcesses(); std::vector GetThreadsInProcess(pid_t pid); bool GetProcessForThread(pid_t tid, pid_t* pid); diff --git a/simpleperf/event_selection_set.cpp b/simpleperf/event_selection_set.cpp index 6c45f150..038997fb 100644 --- a/simpleperf/event_selection_set.cpp +++ b/simpleperf/event_selection_set.cpp @@ -466,14 +466,13 @@ bool EventSelectionSet::MmapEventFiles(size_t mmap_pages, bool report_error) { return true; } -bool EventSelectionSet::PrepareToReadMmapEventData( - IOEventLoop& loop, const std::function& callback) { +bool EventSelectionSet::PrepareToReadMmapEventData(const std::function& callback) { // Add read Events for perf event files having mapped buffer. for (auto& group : groups_) { for (auto& selection : group) { for (auto& event_fd : selection.event_fds) { if (event_fd->HasMappedBuffer()) { - if (!event_fd->StartPolling(loop, [this]() { + if (!event_fd->StartPolling(*loop_, [this]() { return ReadMmapEventData(); })) { return false; @@ -482,7 +481,6 @@ bool EventSelectionSet::PrepareToReadMmapEventData( } } } - loop_ = &loop; // Prepare record callback function. record_callback_ = callback; @@ -568,13 +566,12 @@ bool EventSelectionSet::FinishReadMmapEventData() { return ReadMmapEventData(); } -bool EventSelectionSet::HandleCpuHotplugEvents( - IOEventLoop& loop, const std::vector& monitored_cpus, - double check_interval_in_sec) { +bool EventSelectionSet::HandleCpuHotplugEvents(const std::vector& monitored_cpus, + double check_interval_in_sec) { monitored_cpus_.insert(monitored_cpus.begin(), monitored_cpus.end()); online_cpus_ = GetOnlineCpus(); - if (!loop.AddPeriodicEvent(SecondToTimeval(check_interval_in_sec), - [&]() { return DetectCpuHotplugEvents(); })) { + if (!loop_->AddPeriodicEvent(SecondToTimeval(check_interval_in_sec), + [&]() { return DetectCpuHotplugEvents(); })) { return false; } return true; @@ -719,3 +716,22 @@ bool EventSelectionSet::CreateMappedBufferForCpu(int cpu) { } return true; } + +bool EventSelectionSet::StopWhenNoMoreTargets(double check_interval_in_sec) { + return loop_->AddPeriodicEvent(SecondToTimeval(check_interval_in_sec), + [&]() { return CheckMonitoredTargets(); }); +} + +bool EventSelectionSet::CheckMonitoredTargets() { + for (const auto& tid : threads_) { + if (IsThreadAlive(tid)) { + return true; + } + } + for (const auto& pid : processes_) { + if (IsThreadAlive(pid)) { + return true; + } + } + return loop_->ExitLoop(); +} diff --git a/simpleperf/event_selection_set.h b/simpleperf/event_selection_set.h index b82b9177..8aca7840 100644 --- a/simpleperf/event_selection_set.h +++ b/simpleperf/event_selection_set.h @@ -32,6 +32,7 @@ #include "record.h" constexpr double DEFAULT_PERIOD_TO_DETECT_CPU_HOTPLUG_EVENTS_IN_SEC = 0.5; +constexpr double DEFAULT_PERIOD_TO_CHECK_MONITORED_TARGETS_IN_SEC = 1; struct CounterInfo { pid_t tid; @@ -103,18 +104,24 @@ class EventSelectionSet { return !processes_.empty() || !threads_.empty(); } + void SetIOEventLoop(IOEventLoop& loop) { + loop_ = &loop; + } + bool OpenEventFiles(const std::vector& on_cpus); bool ReadCounters(std::vector* counters); bool MmapEventFiles(size_t min_mmap_pages, size_t max_mmap_pages); - bool PrepareToReadMmapEventData(IOEventLoop& loop, - const std::function& callback); + bool PrepareToReadMmapEventData(const std::function& callback); bool FinishReadMmapEventData(); // If monitored_cpus is empty, monitor all cpus. - bool HandleCpuHotplugEvents( - IOEventLoop& loop, const std::vector& monitored_cpus, - double check_interval_in_sec = - DEFAULT_PERIOD_TO_DETECT_CPU_HOTPLUG_EVENTS_IN_SEC); + bool HandleCpuHotplugEvents(const std::vector& monitored_cpus, + double check_interval_in_sec = + DEFAULT_PERIOD_TO_DETECT_CPU_HOTPLUG_EVENTS_IN_SEC); + + // Stop profiling if all monitored processes/threads don't exist. + bool StopWhenNoMoreTargets(double check_interval_in_sec = + DEFAULT_PERIOD_TO_CHECK_MONITORED_TARGETS_IN_SEC); private: struct EventSelection { @@ -139,6 +146,7 @@ class EventSelectionSet { bool HandleCpuOnlineEvent(int cpu); bool HandleCpuOfflineEvent(int cpu); bool CreateMappedBufferForCpu(int cpu); + bool CheckMonitoredTargets(); const bool for_stat_cmd_; -- cgit v1.2.3