aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKalesh Singh <kaleshsingh@google.com>2022-06-15 16:01:05 -0700
committerKalesh Singh <kaleshsingh@google.com>2022-06-22 13:19:12 -0700
commit1ab43b2d4bfdd1dc6c0865e92e6b1ce0016d9fa8 (patch)
tree049210e71a2c5f3acd20be51f40e4c27f7e49b71
parenteee1f322a6eeed46b3d8ef1a08bf40ff85dd3e2a (diff)
downloadperfetto-1ab43b2d4bfdd1dc6c0865e92e6b1ce0016d9fa8.tar.gz
Using a persistent event trigger for rss_stat_throttled was found to cause regression of 8% more CPU cycles in kswapd. Have perfetto dynamically create these triggers when needed and cleanup the trigger once the perfetto session no longer needs it. Triggers are created/removed when the associated trace event is enabled/disabled by perfetto, so multiple sessions with the same event trigger are inherently handled correctly by the same ftrace_config_muxer logic for enabling/disabling ftrace events. The persistent rss_stat_throttled histogram trigger will be changed to an as-needed trigger created by perfetto. So the existence of this event trigger can no longer be used to check if the kernel supports throttling rss_stat event. Instead test creating a histogram trigger with division arithmetic (needed for event throttling) on rss_stat_throttled. Creating the trigger on rss_stat_throttled also implicitly checks that the synthetic throttled event is present. Test: adb push test/configs/rss_stat.cfg /data/local/tmp/ && cf-adb shell 'cat /data/local/tmp/rss_stat.cfg | perfetto --txt -c - -o /data/misc/perfetto-traces/trace Bug: 236172293 Change-Id: I9e14d56bb442129ea85163e492e00382eb8d8994 Merged-In: I9e14d56bb442129ea85163e492e00382eb8d8994 Merged-In: Ifa50c9e08893c8494739747fe77ab1ceec5c0dfd
-rw-r--r--src/traced/probes/ftrace/ftrace_config_muxer.cc10
-rw-r--r--src/traced/probes/ftrace/ftrace_procfs.cc132
-rw-r--r--src/traced/probes/ftrace/ftrace_procfs.h31
3 files changed, 155 insertions, 18 deletions
diff --git a/src/traced/probes/ftrace/ftrace_config_muxer.cc b/src/traced/probes/ftrace/ftrace_config_muxer.cc
index 6d3a54651..69c4176f3 100644
--- a/src/traced/probes/ftrace/ftrace_config_muxer.cc
+++ b/src/traced/probes/ftrace/ftrace_config_muxer.cc
@@ -95,12 +95,6 @@ void IntersectInPlace(const std::vector<std::string>& unsorted_a,
*out = std::move(v);
}
-bool SupportsRssStatThrottled(const FtraceProcfs& ftrace_procfs) {
- const auto trigger_info = ftrace_procfs.ReadEventTrigger("kmem", "rss_stat");
-
- return trigger_info.find("rss_stat_throttled") != std::string::npos;
-}
-
// This is just to reduce binary size and stack frame size of the insertions.
// It effectively undoes STL's set::insert inlining.
void PERFETTO_NO_INLINE InsertEvent(const char* group,
@@ -416,7 +410,7 @@ std::set<GroupAndName> FtraceConfigMuxer::GetFtraceEvents(
if (category == "memory") {
// Use rss_stat_throttled if supported
- if (SupportsRssStatThrottled(*ftrace_)) {
+ if (ftrace_->SupportsRssStatThrottled()) {
InsertEvent("synthetic", "rss_stat_throttled", &events);
} else {
InsertEvent("kmem", "rss_stat", &events);
@@ -440,7 +434,7 @@ std::set<GroupAndName> FtraceConfigMuxer::GetFtraceEvents(
}
// If throttle_rss_stat: true, use the rss_stat_throttled event if supported
- if (request.throttle_rss_stat() && SupportsRssStatThrottled(*ftrace_)) {
+ if (request.throttle_rss_stat() && ftrace_->SupportsRssStatThrottled()) {
auto it = std::find_if(
events.begin(), events.end(), [](const GroupAndName& event) {
return event.group() == "kmem" && event.name() == "rss_stat";
diff --git a/src/traced/probes/ftrace/ftrace_procfs.cc b/src/traced/probes/ftrace/ftrace_procfs.cc
index e3936488c..55b81b4a4 100644
--- a/src/traced/probes/ftrace/ftrace_procfs.cc
+++ b/src/traced/probes/ftrace/ftrace_procfs.cc
@@ -45,6 +45,12 @@ namespace perfetto {
namespace {
+namespace {
+constexpr char kRssStatThrottledTrigger[] =
+ "hist:keys=mm_id,member:bucket=size/0x80000"
+ ":onchange($bucket).rss_stat_throttled(mm_id,curr,member,size)";
+}
+
void KernelLogWrite(const char* s) {
PERFETTO_DCHECK(*s && s[strlen(s) - 1] == '\n');
if (FtraceProcfs::g_kmesg_fd != -1)
@@ -104,6 +110,11 @@ FtraceProcfs::~FtraceProcfs() = default;
bool FtraceProcfs::EnableEvent(const std::string& group,
const std::string& name) {
std::string path = root_ + "events/" + group + "/" + name + "/enable";
+
+ // Create any required triggers for the ftrace event being enabled.
+ // Some ftrace events (synthetic events) need to set up an event trigger
+ MaybeSetUpEventTriggers(group, name);
+
if (WriteToFile(path, "1"))
return true;
path = root_ + "set_event";
@@ -113,10 +124,17 @@ bool FtraceProcfs::EnableEvent(const std::string& group,
bool FtraceProcfs::DisableEvent(const std::string& group,
const std::string& name) {
std::string path = root_ + "events/" + group + "/" + name + "/enable";
- if (WriteToFile(path, "0"))
- return true;
- path = root_ + "set_event";
- return AppendToFile(path, "!" + group + ":" + name);
+
+ bool ret = WriteToFile(path, "0");
+ if (!ret) {
+ path = root_ + "set_event";
+ ret = AppendToFile(path, "!" + group + ":" + name);
+ }
+
+ // Remove any associated event triggers after disabling the event
+ MaybeTearDownEventTriggers(group, name);
+
+ return ret;
}
bool FtraceProcfs::DisableAllEvents() {
@@ -130,10 +148,110 @@ std::string FtraceProcfs::ReadEventFormat(const std::string& group,
return ReadFileIntoString(path);
}
-std::string FtraceProcfs::ReadEventTrigger(const std::string& group,
- const std::string& name) const {
+std::vector<std::string> FtraceProcfs::ReadEventTriggers(
+ const std::string& group,
+ const std::string& name) const {
std::string path = root_ + "events/" + group + "/" + name + "/trigger";
- return ReadFileIntoString(path);
+ std::string s = ReadFileIntoString(path);
+ std::vector<std::string> triggers;
+
+ for (base::StringSplitter ss(s, '\n'); ss.Next();) {
+ std::string trigger = ss.cur_token();
+ if (trigger.empty() || trigger[0] == '#')
+ continue;
+
+ base::StringSplitter ts(trigger, ' ');
+ PERFETTO_CHECK(ts.Next());
+ triggers.push_back(ts.cur_token());
+ }
+
+ return triggers;
+}
+
+bool FtraceProcfs::CreateEventTrigger(const std::string& group,
+ const std::string& name,
+ const std::string& trigger) {
+ std::string path = root_ + "events/" + group + "/" + name + "/trigger";
+ return WriteToFile(path, trigger);
+}
+
+bool FtraceProcfs::RemoveEventTrigger(const std::string& group,
+ const std::string& name,
+ const std::string& trigger) {
+ std::string path = root_ + "events/" + group + "/" + name + "/trigger";
+ return WriteToFile(path, "!" + trigger);
+}
+
+bool FtraceProcfs::RemoveAllEventTriggers(const std::string& group,
+ const std::string& name) {
+ std::vector<std::string> triggers = ReadEventTriggers(group, name);
+
+ // Remove the triggers in reverse order since a trigger can depend
+ // on another trigger created earlier.
+ for (auto it = triggers.rbegin(); it != triggers.rend(); ++it)
+ if (!RemoveEventTrigger(group, name, *it))
+ return false;
+ return true;
+}
+
+bool FtraceProcfs::MaybeSetUpEventTriggers(const std::string& group,
+ const std::string& name) {
+ bool ret = true;
+
+ if (group == "synthetic" && name == "rss_stat_throttled") {
+ ret = RemoveAllEventTriggers("kmem", "rss_stat") &&
+ CreateEventTrigger("kmem", "rss_stat", kRssStatThrottledTrigger);
+ }
+
+ if (!ret) {
+ PERFETTO_PLOG("Failed to setup event triggers for %s:%s", group.c_str(),
+ name.c_str());
+ }
+
+ return ret;
+}
+
+bool FtraceProcfs::MaybeTearDownEventTriggers(const std::string& group,
+ const std::string& name) {
+ bool ret = true;
+
+ if (group == "synthetic" && name == "rss_stat_throttled")
+ ret = RemoveAllEventTriggers("kmem", "rss_stat");
+
+ if (!ret) {
+ PERFETTO_PLOG("Failed to tear down event triggers for: %s:%s",
+ group.c_str(), name.c_str());
+ }
+
+ return ret;
+}
+
+bool FtraceProcfs::SupportsRssStatThrottled() {
+ std::string group = "synthetic";
+ std::string name = "rss_stat_throttled";
+
+ // Check if the trigger already exists. Don't try recreating
+ // or removing the trigger if it is already in use.
+ auto triggers = ReadEventTriggers("kmem", "rss_stat");
+ for (const auto& trigger : triggers) {
+ // The kernel shows all the default values of a trigger
+ // when read from and trace event 'trigger' file.
+ //
+ // Trying to match the complete trigger string is prone
+ // to fail if, in the future, the kernel changes default
+ // fields or values for event triggers.
+ //
+ // Do a partial match on the generated event name
+ // (rss_stat_throttled) to detect if the trigger
+ // is already created.
+ if (trigger.find(name) != std::string::npos)
+ return true;
+ }
+
+ // Attempt to create rss_stat_throttled hist trigger */
+ bool ret = MaybeSetUpEventTriggers(group, name);
+
+ return ret && MaybeTearDownEventTriggers(group, name);
}
std::string FtraceProcfs::ReadPrintkFormats() const {
diff --git a/src/traced/probes/ftrace/ftrace_procfs.h b/src/traced/probes/ftrace/ftrace_procfs.h
index 6e0791b84..b7ddf990b 100644
--- a/src/traced/probes/ftrace/ftrace_procfs.h
+++ b/src/traced/probes/ftrace/ftrace_procfs.h
@@ -58,9 +58,34 @@ class FtraceProcfs {
virtual std::string ReadPageHeaderFormat() const;
- // Read the triggers for event with the given |group| and |name|.
- std::string ReadEventTrigger(const std::string& group,
- const std::string& name) const;
+ // Get all triggers for event with the given |group| and |name|.
+ std::vector<std::string> ReadEventTriggers(const std::string& group,
+ const std::string& name) const;
+
+ // Create an event trigger for the given |group| and |name|.
+ bool CreateEventTrigger(const std::string& group,
+ const std::string& name,
+ const std::string& trigger);
+
+ // Remove an event trigger for the given |group| and |name|.
+ bool RemoveEventTrigger(const std::string& group,
+ const std::string& name,
+ const std::string& trigger);
+
+ // Remove all event trigger for the given |group| and |name|.
+ bool RemoveAllEventTriggers(const std::string& group,
+ const std::string& name);
+
+ // Sets up any associated event trigger before enabling the event
+ bool MaybeSetUpEventTriggers(const std::string& group,
+ const std::string& name);
+
+ // Tears down any associated event trigger after disabling the event
+ bool MaybeTearDownEventTriggers(const std::string& group,
+ const std::string& name);
+
+ // Returns true if rss_stat_throttled synthetic event is supported
+ bool SupportsRssStatThrottled();
// Read the printk formats file.
std::string ReadPrintkFormats() const;