--- /dev/null
+#
+# Copyright (C) 2015 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+simpleperf_src_files := \
+ cmd_help.cpp \
+ cmd_list.cpp \
+ command.cpp \
+ event_attr.cpp \
+ event_fd.cpp \
+ event_type.cpp \
+ main.cpp \
+ utils.cpp \
+
+simpleperf_cppflags := -std=c++11 -Wall -Wextra -Werror -Wunused
+
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_CPPFLAGS := $(simpleperf_cppflags)
+LOCAL_SRC_FILES := $(simpleperf_src_files)
+LOCAL_STATIC_LIBRARIES := libbase libcutils liblog
+LOCAL_MODULE := simpleperf
+LOCAL_MODULE_TAGS := optional
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_EXECUTABLE)
+
+ifeq ($(HOST_OS),linux)
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_CPPFLAGS := $(simpleperf_cppflags)
+LOCAL_SRC_FILES := $(simpleperf_src_files)
+LOCAL_STATIC_LIBRARIES := libbase libcutils liblog
+LOCAL_LDLIBS := -lrt
+LOCAL_MODULE := simpleperf
+LOCAL_MODULE_TAGS := optional
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_HOST_EXECUTABLE)
+endif
--- /dev/null
+/*
+ * Copyright (C) 2015 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 <stdio.h>
+#include <string>
+#include <vector>
+
+#include <base/logging.h>
+
+#include "command.h"
+
+class HelpCommand : public Command {
+ public:
+ HelpCommand()
+ : Command("help", "print help information for simpleperf",
+ "Usage: simpleperf help [subcommand]\n"
+ " Without subcommand, print short help string for every subcommand.\n"
+ " With subcommand, print long help string for the subcommand.\n\n") {
+ }
+
+ bool Run(const std::vector<std::string>& args) override;
+
+ private:
+ void PrintShortHelp();
+ void PrintLongHelpForOneCommand(const Command& cmd);
+};
+
+bool HelpCommand::Run(const std::vector<std::string>& args) {
+ if (args.empty()) {
+ PrintShortHelp();
+ } else {
+ Command* cmd = Command::FindCommandByName(args[0]);
+ if (cmd == nullptr) {
+ LOG(ERROR) << "malformed command line: can't find help string for unknown command " << args[0];
+ LOG(ERROR) << "try using \"--help\"";
+ return false;
+ } else {
+ PrintLongHelpForOneCommand(*cmd);
+ }
+ }
+ return true;
+}
+
+void HelpCommand::PrintShortHelp() {
+ printf("Usage: simpleperf [--help] subcommand [args_for_subcommand]\n\n");
+ for (auto& command : Command::GetAllCommands()) {
+ printf("%-20s%s\n", command->Name().c_str(), command->ShortHelpString().c_str());
+ }
+}
+
+void HelpCommand::PrintLongHelpForOneCommand(const Command& command) {
+ printf("%s\n", command.LongHelpString().c_str());
+}
+
+HelpCommand help_command;
--- /dev/null
+/*
+ * Copyright (C) 2015 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 <stdio.h>
+#include <string>
+#include <vector>
+
+#include <base/logging.h>
+
+#include "command.h"
+#include "event_type.h"
+#include "perf_event.h"
+
+class ListCommand : public Command {
+ public:
+ ListCommand()
+ : Command("list", "list all available perf events",
+ "Usage: simpleperf list\n"
+ " List all available perf events on this machine.\n") {
+ }
+
+ bool Run(const std::vector<std::string>& args) override;
+
+ private:
+ void PrintEventTypesOfType(uint32_t type, const char* type_name,
+ const std::vector<const EventType>& event_types);
+};
+
+bool ListCommand::Run(const std::vector<std::string>& args) {
+ if (!args.empty()) {
+ LOG(ERROR) << "malformed command line: list subcommand needs no argument";
+ LOG(ERROR) << "try using \"help list\"";
+ return false;
+ }
+ auto& event_types = EventTypeFactory::GetAllEventTypes();
+
+ PrintEventTypesOfType(PERF_TYPE_HARDWARE, "hardware events", event_types);
+ PrintEventTypesOfType(PERF_TYPE_SOFTWARE, "software events", event_types);
+ PrintEventTypesOfType(PERF_TYPE_HW_CACHE, "hw-cache events", event_types);
+ return true;
+}
+
+void ListCommand::PrintEventTypesOfType(uint32_t type, const char* type_name,
+ const std::vector<const EventType>& event_types) {
+ printf("List of %s:\n", type_name);
+ for (auto& event_type : event_types) {
+ if (event_type.type == type && event_type.IsSupportedByKernel()) {
+ printf(" %s\n", event_type.name);
+ }
+ }
+ printf("\n");
+}
+
+ListCommand list_command;
--- /dev/null
+/*
+ * Copyright (C) 2015 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 "command.h"
+
+#include <algorithm>
+#include <string>
+#include <vector>
+
+static std::vector<Command*>& Commands() {
+ // commands is used in the constructor of Command. Defining it as a static
+ // variable in a function makes sure it is initialized before use.
+ static std::vector<Command*> commands;
+ return commands;
+}
+
+Command* Command::FindCommandByName(const std::string& cmd_name) {
+ for (auto command : Commands()) {
+ if (command->Name() == cmd_name) {
+ return command;
+ }
+ }
+ return nullptr;
+}
+
+static bool CompareCommandByName(Command* cmd1, Command* cmd2) {
+ return cmd1->Name() < cmd2->Name();
+}
+
+const std::vector<Command*>& Command::GetAllCommands() {
+ std::sort(Commands().begin(), Commands().end(), CompareCommandByName);
+ return Commands();
+}
+
+void Command::RegisterCommand(Command* cmd) {
+ Commands().push_back(cmd);
+}
--- /dev/null
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef SIMPLE_PERF_COMMAND_H_
+#define SIMPLE_PERF_COMMAND_H_
+
+#include <string>
+#include <vector>
+
+#include <base/macros.h>
+
+class Command {
+ public:
+ Command(const std::string& name, const std::string& short_help_string,
+ const std::string& long_help_string)
+ : name_(name), short_help_string_(short_help_string), long_help_string_(long_help_string) {
+ RegisterCommand(this);
+ }
+
+ virtual ~Command() {
+ }
+
+ const std::string& Name() const {
+ return name_;
+ }
+
+ const std::string& ShortHelpString() const {
+ return short_help_string_;
+ }
+
+ const std::string LongHelpString() const {
+ return long_help_string_;
+ }
+
+ virtual bool Run(const std::vector<std::string>& args) = 0;
+
+ static Command* FindCommandByName(const std::string& cmd_name);
+ static const std::vector<Command*>& GetAllCommands();
+
+ private:
+ const std::string name_;
+ const std::string short_help_string_;
+ const std::string long_help_string_;
+
+ static void RegisterCommand(Command* cmd);
+
+ DISALLOW_COPY_AND_ASSIGN(Command);
+};
+
+#endif // SIMPLE_PERF_COMMAND_H_
--- /dev/null
+/*
+ * Copyright (C) 2015 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 "event_attr.h"
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <string>
+#include <unordered_map>
+
+#include <base/logging.h>
+
+#include "event_type.h"
+#include "utils.h"
+
+static std::string SampleTypeToString(uint64_t sample_type) {
+ std::unordered_map<int, std::string> map = {
+ {PERF_SAMPLE_IP, "ip"},
+ {PERF_SAMPLE_TID, "tid"},
+ {PERF_SAMPLE_TIME, "time"},
+ {PERF_SAMPLE_ADDR, "addr"},
+ {PERF_SAMPLE_READ, "read"},
+ {PERF_SAMPLE_CALLCHAIN, "callchain"},
+ {PERF_SAMPLE_ID, "id"},
+ {PERF_SAMPLE_CPU, "cpu"},
+ {PERF_SAMPLE_PERIOD, "period"},
+ {PERF_SAMPLE_STREAM_ID, "stream_id"},
+ {PERF_SAMPLE_RAW, "raw"},
+ };
+
+ std::string result;
+ for (auto p : map) {
+ if (sample_type & p.first) {
+ sample_type &= ~p.first;
+ if (!result.empty()) {
+ result += ", ";
+ }
+ result += p.second;
+ }
+ }
+ if (sample_type != 0) {
+ LOG(DEBUG) << "unknown sample_type bits: " << std::hex << sample_type;
+ }
+
+ return result;
+}
+
+EventAttr EventAttr::CreateDefaultAttrToMonitorEvent(const EventType& event_type) {
+ perf_event_attr attr;
+ memset(&attr, 0, sizeof(attr));
+ attr.size = sizeof(perf_event_attr);
+ attr.type = event_type.type;
+ attr.config = event_type.config;
+ attr.mmap = 1;
+ attr.comm = 1;
+ attr.read_format =
+ PERF_FORMAT_TOTAL_TIME_ENABLED | PERF_FORMAT_TOTAL_TIME_RUNNING | PERF_FORMAT_ID;
+ attr.sample_type |= PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_PERIOD;
+ return EventAttr(attr);
+}
+
+void EventAttr::Dump(size_t indent) const {
+ std::string event_name = "unknown";
+ const EventType* event_type = EventTypeFactory::FindEventTypeByConfig(attr_.type, attr_.config);
+ if (event_type != nullptr) {
+ event_name = event_type->name;
+ }
+
+ PrintIndented(indent, "event_attr_: for event %s\n", event_name.c_str());
+
+ PrintIndented(indent + 2, "type %u, size %u, config %llu\n", attr_.type, attr_.size, attr_.config);
+
+ if (attr_.freq != 0) {
+ PrintIndented(indent + 2, "sample_freq %llu\n", attr_.sample_freq);
+ } else {
+ PrintIndented(indent + 2, "sample_period %llu\n", attr_.sample_period);
+ }
+
+ PrintIndented(indent + 2, "sample_type (0x%llx) %s\n", attr_.sample_type,
+ SampleTypeToString(attr_.sample_type).c_str());
+
+ PrintIndented(indent + 2, "read_format (0x%llx)\n", attr_.read_format);
+
+ PrintIndented(indent + 2, "disabled %llu, inherit %llu, pinned %llu, exclusive %llu\n",
+ attr_.disabled, attr_.inherit, attr_.pinned, attr_.exclusive);
+
+ PrintIndented(indent + 2, "exclude_user %llu, exclude_kernel %llu, exclude_hv %llu\n",
+ attr_.exclude_user, attr_.exclude_kernel, attr_.exclude_hv);
+
+ PrintIndented(indent + 2, "exclude_idle %llu, mmap %llu, comm %llu, freq %llu\n",
+ attr_.exclude_idle, attr_.mmap, attr_.comm, attr_.freq);
+
+ PrintIndented(indent + 2, "inherit_stat %llu, enable_on_exec %llu, task %llu\n",
+ attr_.inherit_stat, attr_.enable_on_exec, attr_.task);
+
+ PrintIndented(indent + 2, "watermark %llu, precise_ip %llu, mmap_data %llu\n", attr_.watermark,
+ attr_.precise_ip, attr_.mmap_data);
+
+ PrintIndented(indent + 2, "sample_id_all %llu, exclude_host %llu, exclude_guest %llu\n",
+ attr_.sample_id_all, attr_.exclude_host, attr_.exclude_guest);
+}
--- /dev/null
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef SIMPLE_PERF_EVENT_ATTR_H_
+#define SIMPLE_PERF_EVENT_ATTR_H_
+
+#include <stdint.h>
+#include <string>
+
+#include "perf_event.h"
+
+struct EventType;
+
+// EventAttr manages perf_event_attr, which provides detailed configuration information when
+// opening a perf_event_file. The configuration information tells the kernel how to count and
+// record events.
+class EventAttr {
+ public:
+ static EventAttr CreateDefaultAttrToMonitorEvent(const EventType& event_type);
+
+ EventAttr(const perf_event_attr& attr) : attr_(attr) {
+ }
+
+ perf_event_attr Attr() const {
+ return attr_;
+ }
+
+ uint64_t SampleType() const {
+ return attr_.sample_type;
+ }
+
+ void EnableOnExec() {
+ attr_.enable_on_exec = 1;
+ }
+
+ void SetSampleFreq(uint64_t freq) {
+ attr_.freq = 1;
+ attr_.sample_freq = freq;
+ }
+
+ void SetSamplePeriod(uint64_t period) {
+ attr_.freq = 0;
+ attr_.sample_period = period;
+ }
+
+ void SetSampleAll() {
+ attr_.sample_id_all = 1;
+ }
+
+ void Dump(size_t indent = 0) const;
+
+ private:
+ perf_event_attr attr_;
+};
+
+#endif // SIMPLE_PERF_EVENT_ATTR_H_
--- /dev/null
+/*
+ * Copyright (C) 2015 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 "event_fd.h"
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <memory>
+
+#include <base/logging.h>
+#include <base/stringprintf.h>
+
+#include "event_type.h"
+#include "event_attr.h"
+#include "perf_event.h"
+#include "utils.h"
+
+static int perf_event_open(perf_event_attr* attr, pid_t pid, int cpu, int group_fd,
+ unsigned long flags) {
+ return syscall(__NR_perf_event_open, attr, pid, cpu, group_fd, flags);
+}
+
+std::unique_ptr<EventFd> EventFd::OpenEventFileForProcess(const EventAttr& attr, pid_t pid) {
+ return OpenEventFile(attr, pid, -1);
+}
+
+std::unique_ptr<EventFd> EventFd::OpenEventFileForCpu(const EventAttr& attr, int cpu) {
+ return OpenEventFile(attr, -1, cpu);
+}
+
+std::unique_ptr<EventFd> EventFd::OpenEventFile(const EventAttr& attr, pid_t pid, int cpu) {
+ perf_event_attr perf_attr = attr.Attr();
+ std::string event_name = "unknown event";
+ const EventType* event_type =
+ EventTypeFactory::FindEventTypeByConfig(perf_attr.type, perf_attr.config);
+ if (event_type != nullptr) {
+ event_name = event_type->name;
+ }
+ int perf_event_fd = perf_event_open(&perf_attr, pid, cpu, -1, 0);
+ if (perf_event_fd == -1) {
+ // It depends whether the perf_event_file configuration is supported by the kernel and the
+ // machine. So fail to open the file is not an error.
+ PLOG(DEBUG) << "open perf_event_file (event " << event_name << ", pid " << pid << ", cpu "
+ << cpu << ") failed";
+ return nullptr;
+ }
+ if (fcntl(perf_event_fd, F_SETFD, FD_CLOEXEC) == -1) {
+ PLOG(ERROR) << "fcntl(FD_CLOEXEC) for perf_event_file (event " << event_name << ", pid " << pid
+ << ", cpu " << cpu << ") failed";
+ return nullptr;
+ }
+ return std::unique_ptr<EventFd>(new EventFd(perf_event_fd, event_name, pid, cpu));
+}
+
+EventFd::~EventFd() {
+ close(perf_event_fd_);
+}
+
+std::string EventFd::Name() const {
+ return android::base::StringPrintf("perf_event_file(event %s, pid %d, cpu %d)",
+ event_name_.c_str(), pid_, cpu_);
+}
+
+bool EventFd::EnableEvent() {
+ int result = ioctl(perf_event_fd_, PERF_EVENT_IOC_ENABLE, 0);
+ if (result < 0) {
+ PLOG(ERROR) << "ioctl(enable) " << Name() << " failed";
+ return false;
+ }
+ return true;
+}
+
+bool EventFd::DisableEvent() {
+ int result = ioctl(perf_event_fd_, PERF_EVENT_IOC_DISABLE, 0);
+ if (result < 0) {
+ PLOG(ERROR) << "ioctl(disable) " << Name() << " failed";
+ return false;
+ }
+ return true;
+}
--- /dev/null
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef SIMPLE_PERF_EVENT_FD_H_
+#define SIMPLE_PERF_EVENT_FD_H_
+
+#include <sys/types.h>
+
+#include <memory>
+#include <string>
+
+#include <base/macros.h>
+
+class EventAttr;
+
+// EventFd represents an opened perf_event_file.
+class EventFd {
+ public:
+ static std::unique_ptr<EventFd> OpenEventFileForProcess(const EventAttr& attr, pid_t pid);
+ static std::unique_ptr<EventFd> OpenEventFileForCpu(const EventAttr& attr, int cpu);
+ static std::unique_ptr<EventFd> OpenEventFile(const EventAttr& attr, pid_t pid, int cpu);
+
+ ~EventFd();
+
+ // Give information about this perf_event_file, like (event_name, pid, cpu).
+ std::string Name() const;
+
+ // It tells the kernel to start counting and recording events specified by this file.
+ bool EnableEvent();
+
+ // It tells the kernel to stop counting and recording events specified by this file.
+ bool DisableEvent();
+
+ private:
+ EventFd(int perf_event_fd, const std::string& event_name, pid_t pid, int cpu)
+ : perf_event_fd_(perf_event_fd), event_name_(event_name), pid_(pid), cpu_(cpu) {
+ }
+
+ int perf_event_fd_;
+ const std::string event_name_;
+ pid_t pid_;
+ int cpu_;
+
+ DISALLOW_COPY_AND_ASSIGN(EventFd);
+};
+
+#endif // SIMPLE_PERF_EVENT_FD_H_
--- /dev/null
+/*
+ * Copyright (C) 2015 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 "event_type.h"
+
+#include <unistd.h>
+#include <string>
+#include <vector>
+#include "event_attr.h"
+#include "event_fd.h"
+
+#define EVENT_TYPE_TABLE_ENTRY(name, type, config) \
+ { name, type, config } \
+ ,
+
+static std::vector<const EventType> event_type_array = {
+#include "event_type_table.h"
+};
+
+static bool IsEventTypeSupportedByKernel(const EventType& event_type) {
+ auto event_fd = EventFd::OpenEventFileForProcess(
+ EventAttr::CreateDefaultAttrToMonitorEvent(event_type), getpid());
+ return event_fd != nullptr;
+}
+
+bool EventType::IsSupportedByKernel() const {
+ return IsEventTypeSupportedByKernel(*this);
+}
+
+const std::vector<const EventType>& EventTypeFactory::GetAllEventTypes() {
+ return event_type_array;
+}
+
+const EventType* EventTypeFactory::FindEventTypeByName(const std::string& name) {
+ for (auto& event_type : event_type_array) {
+ if (event_type.name == name) {
+ return &event_type;
+ }
+ }
+ return nullptr;
+}
+
+const EventType* EventTypeFactory::FindEventTypeByConfig(uint32_t type, uint64_t config) {
+ for (auto& event_type : event_type_array) {
+ if (event_type.type == type && event_type.config == config) {
+ return &event_type;
+ }
+ }
+ return nullptr;
+}
--- /dev/null
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef SIMPLE_PERF_EVENT_H_
+#define SIMPLE_PERF_EVENT_H_
+
+#include <stdint.h>
+#include <string>
+#include <vector>
+
+// EventType represents one type of event, like cpu_cycle_event, cache_misses_event.
+// The user knows one event type by its name, and the kernel knows one event type by its
+// (type, config) pair. EventType connects the two representations, and tells the user if
+// the event type is supported by the kernel.
+
+struct EventType {
+ bool IsSupportedByKernel() const;
+
+ const char* name;
+ uint32_t type;
+ uint64_t config;
+};
+
+class EventTypeFactory {
+ public:
+ static const std::vector<const EventType>& GetAllEventTypes();
+ static const EventType* FindEventTypeByName(const std::string& name);
+ static const EventType* FindEventTypeByConfig(uint32_t type, uint64_t config);
+};
+
+#endif // SIMPLE_PERF_EVENT_H_
--- /dev/null
+// This file is auto-generated by generate-event_table.py.
+
+{"cpu-cycles", PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES},
+{"instructions", PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS},
+{"cache-references", PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_REFERENCES},
+{"cache-misses", PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES},
+{"branch-instructions", PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_INSTRUCTIONS},
+{"branch-misses", PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES},
+{"bus-cycles", PERF_TYPE_HARDWARE, PERF_COUNT_HW_BUS_CYCLES},
+{"stalled-cycles-frontend", PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND},
+{"stalled-cycles-backend", PERF_TYPE_HARDWARE, PERF_COUNT_HW_STALLED_CYCLES_BACKEND},
+
+{"cpu-clock", PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_CLOCK},
+{"task-clock", PERF_TYPE_SOFTWARE, PERF_COUNT_SW_TASK_CLOCK},
+{"page-faults", PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS},
+{"context-switches", PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CONTEXT_SWITCHES},
+{"cpu-migrations", PERF_TYPE_SOFTWARE, PERF_COUNT_SW_CPU_MIGRATIONS},
+{"minor-faults", PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MIN},
+{"major-faults", PERF_TYPE_SOFTWARE, PERF_COUNT_SW_PAGE_FAULTS_MAJ},
+{"alignment-faults", PERF_TYPE_SOFTWARE, PERF_COUNT_SW_ALIGNMENT_FAULTS},
+{"emulation-faults", PERF_TYPE_SOFTWARE, PERF_COUNT_SW_EMULATION_FAULTS},
+
+{"L1-dcache-loades", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1D) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"L1-dcache-load-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1D) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"L1-dcache-stores", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1D) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"L1-dcache-store-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1D) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"L1-dcache-prefetches", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1D) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"L1-dcache-prefetch-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1D) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"L1-icache-loades", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1I) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"L1-icache-load-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1I) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"L1-icache-stores", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1I) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"L1-icache-store-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1I) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"L1-icache-prefetches", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1I) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"L1-icache-prefetch-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_L1I) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"LLC-loades", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_LL) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"LLC-load-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_LL) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"LLC-stores", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_LL) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"LLC-store-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_LL) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"LLC-prefetches", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_LL) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"LLC-prefetch-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_LL) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"dTLB-loades", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_DTLB) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"dTLB-load-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_DTLB) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"dTLB-stores", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_DTLB) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"dTLB-store-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_DTLB) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"dTLB-prefetches", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_DTLB) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"dTLB-prefetch-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_DTLB) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"iTLB-loades", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_ITLB) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"iTLB-load-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_ITLB) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"iTLB-stores", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_ITLB) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"iTLB-store-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_ITLB) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"iTLB-prefetches", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_ITLB) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"iTLB-prefetch-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_ITLB) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"branch-loades", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_BPU) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"branch-load-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_BPU) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"branch-stores", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_BPU) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"branch-store-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_BPU) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"branch-prefetches", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_BPU) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"branch-prefetch-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_BPU) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"node-loades", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_NODE) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"node-load-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_NODE) | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"node-stores", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_NODE) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"node-store-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_NODE) | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+{"node-prefetches", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_NODE) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))},
+{"node-prefetch-misses", PERF_TYPE_HW_CACHE, ((PERF_COUNT_HW_CACHE_NODE) | (PERF_COUNT_HW_CACHE_OP_PREFETCH << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))},
+
--- /dev/null
+#!/usr/bin/python
+#
+# Copyright (C) 2015 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.
+#
+
+
+def gen_event_type_entry_str(event_type_name, event_type, event_config):
+ """
+ return string like:
+ {"cpu-cycles", PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES},
+ """
+ return '{"%s", %s, %s},\n' % (event_type_name, event_type, event_config)
+
+
+def gen_hardware_events():
+ hardware_configs = ["cpu-cycles",
+ "instructions",
+ "cache-references",
+ "cache-misses",
+ "branch-instructions",
+ "branch-misses",
+ "bus-cycles",
+ "stalled-cycles-frontend",
+ "stalled-cycles-backend",
+ ]
+ generated_str = ""
+ for config in hardware_configs:
+ event_type_name = config
+ event_config = "PERF_COUNT_HW_" + config.replace('-', '_').upper()
+
+ generated_str += gen_event_type_entry_str(
+ event_type_name, "PERF_TYPE_HARDWARE", event_config)
+
+ return generated_str
+
+
+def gen_software_events():
+ software_configs = ["cpu-clock",
+ "task-clock",
+ "page-faults",
+ "context-switches",
+ "cpu-migrations",
+ ["minor-faults", "PERF_COUNT_SW_PAGE_FAULTS_MIN"],
+ ["major-faults", "PERF_COUNT_SW_PAGE_FAULTS_MAJ"],
+ "alignment-faults",
+ "emulation-faults",
+ ]
+ generated_str = ""
+ for config in software_configs:
+ if type(config) is list:
+ event_type_name = config[0]
+ event_config = config[1]
+ else:
+ event_type_name = config
+ event_config = "PERF_COUNT_SW_" + config.replace('-', '_').upper()
+
+ generated_str += gen_event_type_entry_str(
+ event_type_name, "PERF_TYPE_SOFTWARE", event_config)
+
+ return generated_str
+
+
+def gen_hw_cache_events():
+ hw_cache_types = [["L1-dcache", "PERF_COUNT_HW_CACHE_L1D"],
+ ["L1-icache", "PERF_COUNT_HW_CACHE_L1I"],
+ ["LLC", "PERF_COUNT_HW_CACHE_LL"],
+ ["dTLB", "PERF_COUNT_HW_CACHE_DTLB"],
+ ["iTLB", "PERF_COUNT_HW_CACHE_ITLB"],
+ ["branch", "PERF_COUNT_HW_CACHE_BPU"],
+ ["node", "PERF_COUNT_HW_CACHE_NODE"],
+ ]
+ hw_cache_ops = [["loades", "load", "PERF_COUNT_HW_CACHE_OP_READ"],
+ ["stores", "store", "PERF_COUNT_HW_CACHE_OP_WRITE"],
+ ["prefetches", "prefetch",
+ "PERF_COUNT_HW_CACHE_OP_PREFETCH"],
+ ]
+ hw_cache_op_results = [["accesses", "PERF_COUNT_HW_CACHE_RESULT_ACCESS"],
+ ["misses", "PERF_COUNT_HW_CACHE_RESULT_MISS"],
+ ]
+ generated_str = ""
+ for (type_name, type_config) in hw_cache_types:
+ for (op_name_access, op_name_miss, op_config) in hw_cache_ops:
+ for (result_name, result_config) in hw_cache_op_results:
+ if result_name == "accesses":
+ event_type_name = type_name + '-' + op_name_access
+ else:
+ event_type_name = type_name + '-' + \
+ op_name_miss + '-' + result_name
+ event_config = "((%s) | (%s << 8) | (%s << 16))" % (
+ type_config, op_config, result_config)
+ generated_str += gen_event_type_entry_str(
+ event_type_name, "PERF_TYPE_HW_CACHE", event_config)
+
+ return generated_str
+
+
+def gen_events():
+ generated_str = "// This file is auto-generated by generate-event_table.py.\n\n"
+ generated_str += gen_hardware_events() + '\n'
+ generated_str += gen_software_events() + '\n'
+ generated_str += gen_hw_cache_events() + '\n'
+ return generated_str
+
+generated_str = gen_events()
+fh = open('event_type_table.h', 'w')
+fh.write(generated_str)
+fh.close()
--- /dev/null
+/*
+ * Copyright (C) 2015 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 <string.h>
+#include <string>
+#include <vector>
+
+#include <base/logging.h>
+
+#include "command.h"
+
+int main(int argc, char** argv) {
+ InitLogging(argv, android::base::StderrLogger);
+ std::vector<std::string> args;
+
+ if (argc == 1 || (argc == 2 && strcmp(argv[1], "--help") == 0)) {
+ args.push_back("help");
+ } else {
+ for (int i = 1; i < argc; ++i) {
+ args.push_back(argv[i]);
+ }
+ }
+
+ Command* command = Command::FindCommandByName(args[0]);
+ if (command == nullptr) {
+ LOG(ERROR) << "malformed command line: unknown command " << args[0];
+ return 1;
+ }
+ args.erase(args.begin());
+ bool result = command->Run(args);
+ if (result == true) {
+ LOG(DEBUG) << "run command " << args[0] << " successfully";
+ } else {
+ LOG(DEBUG) << "run command " << args[0] << "unsuccessfully";
+ }
+ return result ? 0 : 1;
+}
--- /dev/null
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef SIMPLE_PERF_PERF_EVENT_H_
+#define SIMPLE_PERF_PERF_EVENT_H_
+
+#include <linux/perf_event.h>
+
+#endif // SIMPLE_PERF_PERF_EVENT_H_
--- /dev/null
+/*
+ * Copyright (C) 2015 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 "utils.h"
+
+#include <stdarg.h>
+#include <stdio.h>
+
+void PrintIndented(size_t indent, const char* fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ printf("%*s", static_cast<int>(indent), "");
+ vprintf(fmt, ap);
+ va_end(ap);
+}
--- /dev/null
+/*
+ * Copyright (C) 2015 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.
+ */
+
+#ifndef SIMPLE_PERF_UTILS_H_
+#define SIMPLE_PERF_UTILS_H_
+
+#include <stddef.h>
+
+void PrintIndented(size_t indent, const char* fmt, ...);
+
+#endif // SIMPLE_PERF_UTILS_H_