From: Yabin Cui Date: Thu, 16 Apr 2015 22:26:31 +0000 (-0700) Subject: Implement simpleperf list subcommand. X-Git-Tag: android-x86-6.0-r1~14^2~4^2~1 X-Git-Url: http://git.osdn.net/view?p=android-x86%2Fsystem-extras.git;a=commitdiff_plain;h=67d3abd7b26a741347b33402ad32f5c6735ca0bd Implement simpleperf list subcommand. simpleperf is used to replace linux/tools/perf. And This CL implements the list subcommand of it. Change-Id: I3e6fe854e19cc370070d0fd8416d0aa6fa8f8e90 --- diff --git a/simpleperf/Android.mk b/simpleperf/Android.mk new file mode 100644 index 00000000..22bfd77c --- /dev/null +++ b/simpleperf/Android.mk @@ -0,0 +1,52 @@ +# +# 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 diff --git a/simpleperf/cmd_help.cpp b/simpleperf/cmd_help.cpp new file mode 100644 index 00000000..bf08dba5 --- /dev/null +++ b/simpleperf/cmd_help.cpp @@ -0,0 +1,68 @@ +/* + * 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 +#include +#include + +#include + +#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& args) override; + + private: + void PrintShortHelp(); + void PrintLongHelpForOneCommand(const Command& cmd); +}; + +bool HelpCommand::Run(const std::vector& 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; diff --git a/simpleperf/cmd_list.cpp b/simpleperf/cmd_list.cpp new file mode 100644 index 00000000..dd35c299 --- /dev/null +++ b/simpleperf/cmd_list.cpp @@ -0,0 +1,67 @@ +/* + * 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 +#include +#include + +#include + +#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& args) override; + + private: + void PrintEventTypesOfType(uint32_t type, const char* type_name, + const std::vector& event_types); +}; + +bool ListCommand::Run(const std::vector& 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& 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; diff --git a/simpleperf/command.cpp b/simpleperf/command.cpp new file mode 100644 index 00000000..d26576a3 --- /dev/null +++ b/simpleperf/command.cpp @@ -0,0 +1,50 @@ +/* + * 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 +#include +#include + +static std::vector& 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 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::GetAllCommands() { + std::sort(Commands().begin(), Commands().end(), CompareCommandByName); + return Commands(); +} + +void Command::RegisterCommand(Command* cmd) { + Commands().push_back(cmd); +} diff --git a/simpleperf/command.h b/simpleperf/command.h new file mode 100644 index 00000000..a2e1923a --- /dev/null +++ b/simpleperf/command.h @@ -0,0 +1,63 @@ +/* + * 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 +#include + +#include + +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& args) = 0; + + static Command* FindCommandByName(const std::string& cmd_name); + static const std::vector& 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_ diff --git a/simpleperf/event_attr.cpp b/simpleperf/event_attr.cpp new file mode 100644 index 00000000..a1ee3d94 --- /dev/null +++ b/simpleperf/event_attr.cpp @@ -0,0 +1,114 @@ +/* + * 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 +#include +#include +#include + +#include + +#include "event_type.h" +#include "utils.h" + +static std::string SampleTypeToString(uint64_t sample_type) { + std::unordered_map 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); +} diff --git a/simpleperf/event_attr.h b/simpleperf/event_attr.h new file mode 100644 index 00000000..30052f14 --- /dev/null +++ b/simpleperf/event_attr.h @@ -0,0 +1,69 @@ +/* + * 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 +#include + +#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_ diff --git a/simpleperf/event_fd.cpp b/simpleperf/event_fd.cpp new file mode 100644 index 00000000..7c4ea44c --- /dev/null +++ b/simpleperf/event_fd.cpp @@ -0,0 +1,96 @@ +/* + * 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 +#include +#include +#include +#include +#include + +#include +#include + +#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::OpenEventFileForProcess(const EventAttr& attr, pid_t pid) { + return OpenEventFile(attr, pid, -1); +} + +std::unique_ptr EventFd::OpenEventFileForCpu(const EventAttr& attr, int cpu) { + return OpenEventFile(attr, -1, cpu); +} + +std::unique_ptr 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(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; +} diff --git a/simpleperf/event_fd.h b/simpleperf/event_fd.h new file mode 100644 index 00000000..96286fb3 --- /dev/null +++ b/simpleperf/event_fd.h @@ -0,0 +1,60 @@ +/* + * 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 + +#include +#include + +#include + +class EventAttr; + +// EventFd represents an opened perf_event_file. +class EventFd { + public: + static std::unique_ptr OpenEventFileForProcess(const EventAttr& attr, pid_t pid); + static std::unique_ptr OpenEventFileForCpu(const EventAttr& attr, int cpu); + static std::unique_ptr 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_ diff --git a/simpleperf/event_type.cpp b/simpleperf/event_type.cpp new file mode 100644 index 00000000..01d74573 --- /dev/null +++ b/simpleperf/event_type.cpp @@ -0,0 +1,63 @@ +/* + * 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 +#include +#include +#include "event_attr.h" +#include "event_fd.h" + +#define EVENT_TYPE_TABLE_ENTRY(name, type, config) \ + { name, type, config } \ + , + +static std::vector 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& 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; +} diff --git a/simpleperf/event_type.h b/simpleperf/event_type.h new file mode 100644 index 00000000..e2f21d5d --- /dev/null +++ b/simpleperf/event_type.h @@ -0,0 +1,44 @@ +/* + * 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 +#include +#include + +// 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& GetAllEventTypes(); + static const EventType* FindEventTypeByName(const std::string& name); + static const EventType* FindEventTypeByConfig(uint32_t type, uint64_t config); +}; + +#endif // SIMPLE_PERF_EVENT_H_ diff --git a/simpleperf/event_type_table.h b/simpleperf/event_type_table.h new file mode 100644 index 00000000..895cc85b --- /dev/null +++ b/simpleperf/event_type_table.h @@ -0,0 +1,65 @@ +// 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))}, + diff --git a/simpleperf/generate_event_type_table.py b/simpleperf/generate_event_type_table.py new file mode 100755 index 00000000..b3fb897c --- /dev/null +++ b/simpleperf/generate_event_type_table.py @@ -0,0 +1,119 @@ +#!/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() diff --git a/simpleperf/main.cpp b/simpleperf/main.cpp new file mode 100644 index 00000000..017e4952 --- /dev/null +++ b/simpleperf/main.cpp @@ -0,0 +1,50 @@ +/* + * 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 +#include +#include + +#include + +#include "command.h" + +int main(int argc, char** argv) { + InitLogging(argv, android::base::StderrLogger); + std::vector 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; +} diff --git a/simpleperf/perf_event.h b/simpleperf/perf_event.h new file mode 100644 index 00000000..a91eb6bc --- /dev/null +++ b/simpleperf/perf_event.h @@ -0,0 +1,22 @@ +/* + * 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 + +#endif // SIMPLE_PERF_PERF_EVENT_H_ diff --git a/simpleperf/utils.cpp b/simpleperf/utils.cpp new file mode 100644 index 00000000..2b02bb16 --- /dev/null +++ b/simpleperf/utils.cpp @@ -0,0 +1,28 @@ +/* + * 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 +#include + +void PrintIndented(size_t indent, const char* fmt, ...) { + va_list ap; + va_start(ap, fmt); + printf("%*s", static_cast(indent), ""); + vprintf(fmt, ap); + va_end(ap); +} diff --git a/simpleperf/utils.h b/simpleperf/utils.h new file mode 100644 index 00000000..dcbe74d6 --- /dev/null +++ b/simpleperf/utils.h @@ -0,0 +1,24 @@ +/* + * 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 + +void PrintIndented(size_t indent, const char* fmt, ...); + +#endif // SIMPLE_PERF_UTILS_H_