| /* | 
 |  * Copyright (C) 2017 The Android Open Source Project | 
 |  * | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 |  * you may not use this file except in compliance with the License. | 
 |  * You may obtain a copy of the License at | 
 |  * | 
 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  */ | 
 |  | 
 | #include "src/traced/probes/ftrace/ftrace_procfs.h" | 
 |  | 
 | #include <string.h> | 
 | #include <sys/stat.h> | 
 | #include <sys/types.h> | 
 | #include <unistd.h> | 
 |  | 
 | #include <fstream> | 
 | #include <sstream> | 
 | #include <string> | 
 |  | 
 | #include "perfetto/base/file_utils.h" | 
 | #include "perfetto/base/logging.h" | 
 | #include "perfetto/base/utils.h" | 
 |  | 
 | namespace perfetto { | 
 |  | 
 | // Reading /trace produces human readable trace output. | 
 | // Writing to this file clears all trace buffers for all CPUS. | 
 |  | 
 | // Writing to /trace_marker file injects an event into the trace buffer. | 
 |  | 
 | // Reading /tracing_on returns 1/0 if tracing is enabled/disabled. | 
 | // Writing 1/0 to this file enables/disables tracing. | 
 | // Disabling tracing with this file prevents further writes but | 
 | // does not clear the buffer. | 
 |  | 
 | namespace { | 
 |  | 
 | void KernelLogWrite(const char* s) { | 
 |   PERFETTO_DCHECK(*s && s[strlen(s) - 1] == '\n'); | 
 |   if (FtraceProcfs::g_kmesg_fd != -1) | 
 |     base::ignore_result(base::WriteAll(FtraceProcfs::g_kmesg_fd, s, strlen(s))); | 
 | } | 
 |  | 
 | bool WriteFileInternal(const std::string& path, | 
 |                        const std::string& str, | 
 |                        int flags) { | 
 |   base::ScopedFile fd = base::OpenFile(path, flags); | 
 |   if (!fd) | 
 |     return false; | 
 |   ssize_t written = base::WriteAll(fd.get(), str.c_str(), str.length()); | 
 |   ssize_t length = static_cast<ssize_t>(str.length()); | 
 |   // This should either fail or write fully. | 
 |   PERFETTO_CHECK(written == length || written == -1); | 
 |   return written == length; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | // static | 
 | int FtraceProcfs::g_kmesg_fd = -1;  // Set by ProbesMain() in probes.cc . | 
 |  | 
 | // static | 
 | std::unique_ptr<FtraceProcfs> FtraceProcfs::Create(const std::string& root) { | 
 |   if (!CheckRootPath(root)) { | 
 |     return nullptr; | 
 |   } | 
 |   return std::unique_ptr<FtraceProcfs>(new FtraceProcfs(root)); | 
 | } | 
 |  | 
 | FtraceProcfs::FtraceProcfs(const std::string& root) : root_(root) {} | 
 | FtraceProcfs::~FtraceProcfs() = default; | 
 |  | 
 | bool FtraceProcfs::EnableEvent(const std::string& group, | 
 |                                const std::string& name) { | 
 |   std::string path = root_ + "events/" + group + "/" + name + "/enable"; | 
 |   if (WriteToFile(path, "1")) | 
 |     return true; | 
 |   path = root_ + "set_event"; | 
 |   return AppendToFile(path, group + ":" + name); | 
 | } | 
 |  | 
 | 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 FtraceProcfs::DisableAllEvents() { | 
 |   std::string path = root_ + "events/enable"; | 
 |   return WriteToFile(path, "0"); | 
 | } | 
 |  | 
 | std::string FtraceProcfs::ReadEventFormat(const std::string& group, | 
 |                                           const std::string& name) const { | 
 |   std::string path = root_ + "events/" + group + "/" + name + "/format"; | 
 |   return ReadFileIntoString(path); | 
 | } | 
 |  | 
 | std::string FtraceProcfs::ReadPageHeaderFormat() const { | 
 |   std::string path = root_ + "events/header_page"; | 
 |   return ReadFileIntoString(path); | 
 | } | 
 |  | 
 | std::string FtraceProcfs::ReadCpuStats(size_t cpu) const { | 
 |   std::string path = root_ + "per_cpu/cpu" + std::to_string(cpu) + "/stats"; | 
 |   return ReadFileIntoString(path); | 
 | } | 
 |  | 
 | size_t FtraceProcfs::NumberOfCpus() const { | 
 |   static size_t num_cpus = static_cast<size_t>(sysconf(_SC_NPROCESSORS_CONF)); | 
 |   return num_cpus; | 
 | } | 
 |  | 
 | void FtraceProcfs::ClearTrace() { | 
 |   std::string path = root_ + "trace"; | 
 |   PERFETTO_CHECK(ClearFile(path));  // Could not clear. | 
 |  | 
 |   // Truncating the trace file leads to tracing_reset_online_cpus being called | 
 |   // in the kernel. | 
 |   // | 
 |   // In case some of the CPUs were not online, their buffer needs to be | 
 |   // cleared manually. | 
 |   // | 
 |   // We cannot use PERFETTO_CHECK as we might get a permission denied error | 
 |   // on Android. The permissions to these files are configured in | 
 |   // platform/framework/native/cmds/atrace/atrace.rc. | 
 |   for (size_t cpu = 0; cpu < NumberOfCpus(); cpu++) { | 
 |     if (!ClearFile(root_ + "per_cpu/cpu" + std::to_string(cpu) + "/trace")) | 
 |       PERFETTO_ELOG("Failed to clear buffer for CPU %zd", cpu); | 
 |   } | 
 | } | 
 |  | 
 | bool FtraceProcfs::WriteTraceMarker(const std::string& str) { | 
 |   std::string path = root_ + "trace_marker"; | 
 |   return WriteToFile(path, str); | 
 | } | 
 |  | 
 | bool FtraceProcfs::SetCpuBufferSizeInPages(size_t pages) { | 
 |   if (pages * base::kPageSize > 1 * 1024 * 1024 * 1024) { | 
 |     PERFETTO_ELOG("Tried to set the per CPU buffer size to more than 1gb."); | 
 |     return false; | 
 |   } | 
 |   std::string path = root_ + "buffer_size_kb"; | 
 |   return WriteNumberToFile(path, pages * (base::kPageSize / 1024ul)); | 
 | } | 
 |  | 
 | bool FtraceProcfs::EnableTracing() { | 
 |   KernelLogWrite("perfetto: enabled ftrace\n"); | 
 |   std::string path = root_ + "tracing_on"; | 
 |   return WriteToFile(path, "1"); | 
 | } | 
 |  | 
 | bool FtraceProcfs::DisableTracing() { | 
 |   KernelLogWrite("perfetto: disabled ftrace\n"); | 
 |   std::string path = root_ + "tracing_on"; | 
 |   return WriteToFile(path, "0"); | 
 | } | 
 |  | 
 | bool FtraceProcfs::SetTracingOn(bool enable) { | 
 |   return enable ? EnableTracing() : DisableTracing(); | 
 | } | 
 |  | 
 | bool FtraceProcfs::IsTracingEnabled() { | 
 |   std::string path = root_ + "tracing_on"; | 
 |   return ReadOneCharFromFile(path) == '1'; | 
 | } | 
 |  | 
 | bool FtraceProcfs::SetClock(const std::string& clock_name) { | 
 |   std::string path = root_ + "trace_clock"; | 
 |   return WriteToFile(path, clock_name); | 
 | } | 
 |  | 
 | std::string FtraceProcfs::GetClock() { | 
 |   std::string path = root_ + "trace_clock"; | 
 |   std::string s = ReadFileIntoString(path); | 
 |  | 
 |   size_t start = s.find('['); | 
 |   if (start == std::string::npos) | 
 |     return ""; | 
 |  | 
 |   size_t end = s.find(']', start); | 
 |   if (end == std::string::npos) | 
 |     return ""; | 
 |  | 
 |   return s.substr(start + 1, end - start - 1); | 
 | } | 
 |  | 
 | std::set<std::string> FtraceProcfs::AvailableClocks() { | 
 |   std::string path = root_ + "trace_clock"; | 
 |   std::string s = ReadFileIntoString(path); | 
 |   std::set<std::string> names; | 
 |  | 
 |   size_t start = 0; | 
 |   size_t end = 0; | 
 |  | 
 |   for (;;) { | 
 |     end = s.find(' ', start); | 
 |     if (end == std::string::npos) | 
 |       end = s.size(); | 
 |     while (end > start && s[end - 1] == '\n') | 
 |       end--; | 
 |     if (start == end) | 
 |       break; | 
 |  | 
 |     std::string name = s.substr(start, end - start); | 
 |  | 
 |     if (name[0] == '[') | 
 |       name = name.substr(1, name.size() - 2); | 
 |  | 
 |     names.insert(name); | 
 |  | 
 |     if (end == s.size()) | 
 |       break; | 
 |  | 
 |     start = end + 1; | 
 |   } | 
 |  | 
 |   return names; | 
 | } | 
 |  | 
 | bool FtraceProcfs::WriteNumberToFile(const std::string& path, size_t value) { | 
 |   // 2^65 requires 20 digits to write. | 
 |   char buf[21]; | 
 |   int res = snprintf(buf, 21, "%zu", value); | 
 |   if (res < 0 || res >= 21) | 
 |     return false; | 
 |   return WriteToFile(path, std::string(buf)); | 
 | } | 
 |  | 
 | bool FtraceProcfs::WriteToFile(const std::string& path, | 
 |                                const std::string& str) { | 
 |   return WriteFileInternal(path, str, O_WRONLY); | 
 | } | 
 |  | 
 | bool FtraceProcfs::AppendToFile(const std::string& path, | 
 |                                 const std::string& str) { | 
 |   return WriteFileInternal(path, str, O_WRONLY | O_APPEND); | 
 | } | 
 |  | 
 | base::ScopedFile FtraceProcfs::OpenPipeForCpu(size_t cpu) { | 
 |   std::string path = | 
 |       root_ + "per_cpu/cpu" + std::to_string(cpu) + "/trace_pipe_raw"; | 
 |   return base::OpenFile(path, O_RDONLY | O_NONBLOCK); | 
 | } | 
 |  | 
 | char FtraceProcfs::ReadOneCharFromFile(const std::string& path) { | 
 |   base::ScopedFile fd = base::OpenFile(path, O_RDONLY); | 
 |   PERFETTO_CHECK(fd); | 
 |   char result = '\0'; | 
 |   ssize_t bytes = PERFETTO_EINTR(read(fd.get(), &result, 1)); | 
 |   PERFETTO_CHECK(bytes == 1 || bytes == -1); | 
 |   return result; | 
 | } | 
 |  | 
 | bool FtraceProcfs::ClearFile(const std::string& path) { | 
 |   base::ScopedFile fd = base::OpenFile(path, O_WRONLY | O_TRUNC); | 
 |   return !!fd; | 
 | } | 
 |  | 
 | std::string FtraceProcfs::ReadFileIntoString(const std::string& path) const { | 
 |   // You can't seek or stat the procfs files on Android. | 
 |   // The vast majority (884/886) of format files are under 4k. | 
 |   std::string str; | 
 |   str.reserve(4096); | 
 |   if (!base::ReadFile(path, &str)) | 
 |     return ""; | 
 |   return str; | 
 | } | 
 |  | 
 | const std::set<std::string> FtraceProcfs::GetEventNamesForGroup( | 
 |     const std::string& path) const { | 
 |   std::set<std::string> names; | 
 |   std::string full_path = root_ + path; | 
 |   base::ScopedDir dir(opendir(full_path.c_str())); | 
 |   if (!dir) { | 
 |     PERFETTO_DLOG("Unable to read events from %s", full_path.c_str()); | 
 |     return names; | 
 |   } | 
 |   struct dirent* ent; | 
 |   while ((ent = readdir(*dir)) != nullptr) { | 
 |     if (strncmp(ent->d_name, ".", 1) == 0 || | 
 |         strncmp(ent->d_name, "..", 2) == 0) { | 
 |       continue; | 
 |     } | 
 |     // Check ent is a directory. | 
 |     struct stat statbuf; | 
 |     std::string dir_path = full_path + "/" + ent->d_name; | 
 |     if (stat(dir_path.c_str(), &statbuf) == 0) { | 
 |       if (S_ISDIR(statbuf.st_mode)) { | 
 |         names.insert(ent->d_name); | 
 |       } | 
 |     } | 
 |   } | 
 |   return names; | 
 | } | 
 |  | 
 | // static | 
 | bool FtraceProcfs::CheckRootPath(const std::string& root) { | 
 |   base::ScopedFile fd = base::OpenFile(root + "trace", O_RDONLY); | 
 |   return static_cast<bool>(fd); | 
 | } | 
 |  | 
 | }  // namespace perfetto |