Commit e2c9cfd9 authored by Yabin Cui's avatar Yabin Cui Committed by Android Git Automerger
Browse files

am ffc011b6: Merge "Implement simpleperf stat subcommand."

* commit 'ffc011b6':
  Implement simpleperf stat subcommand.
parents 357d7bfb ffc011b6
......@@ -16,22 +16,48 @@
LOCAL_PATH := $(call my-dir)
simpleperf_src_files := \
simpleperf_common_cppflags := -std=c++11 -Wall -Wextra -Werror -Wunused
libsimpleperf_src_files := \
cmd_help.cpp \
cmd_list.cpp \
cmd_stat.cpp \
command.cpp \
environment.cpp \
event_attr.cpp \
event_fd.cpp \
event_type.cpp \
main.cpp \
utils.cpp \
workload.cpp \
include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
LOCAL_SRC_FILES := $(libsimpleperf_src_files)
LOCAL_STATIC_LIBRARIES := libbase libcutils liblog
LOCAL_MODULE := libsimpleperf
LOCAL_MODULE_TAGS := optional
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_STATIC_LIBRARY)
simpleperf_cppflags := -std=c++11 -Wall -Wextra -Werror -Wunused
ifeq ($(HOST_OS),linux)
include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
LOCAL_SRC_FILES := $(libsimpleperf_src_files)
LOCAL_STATIC_LIBRARIES := libbase libcutils liblog
LOCAL_LDLIBS := -lrt
LOCAL_MODULE := libsimpleperf
LOCAL_MODULE_TAGS := optional
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_HOST_STATIC_LIBRARY)
endif
include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_CPPFLAGS := $(simpleperf_cppflags)
LOCAL_SRC_FILES := $(simpleperf_src_files)
LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
LOCAL_SRC_FILES := main.cpp
LOCAL_WHOLE_STATIC_LIBRARIES := libsimpleperf
LOCAL_STATIC_LIBRARIES := libbase libcutils liblog
LOCAL_MODULE := simpleperf
LOCAL_MODULE_TAGS := optional
......@@ -41,8 +67,9 @@ include $(BUILD_EXECUTABLE)
ifeq ($(HOST_OS),linux)
include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_CPPFLAGS := $(simpleperf_cppflags)
LOCAL_SRC_FILES := $(simpleperf_src_files)
LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
LOCAL_SRC_FILES := main.cpp
LOCAL_WHOLE_STATIC_LIBRARIES := libsimpleperf
LOCAL_STATIC_LIBRARIES := libbase libcutils liblog
LOCAL_LDLIBS := -lrt
LOCAL_MODULE := simpleperf
......@@ -50,3 +77,35 @@ LOCAL_MODULE_TAGS := optional
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_HOST_EXECUTABLE)
endif
simpleperf_unit_test_src_files := \
cmd_list_test.cpp \
cmd_stat_test.cpp \
command_test.cpp \
environment_test.cpp \
gtest_main.cpp \
workload_test.cpp \
include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
LOCAL_SRC_FILES := $(simpleperf_unit_test_src_files)
LOCAL_WHOLE_STATIC_LIBRARIES := libsimpleperf
LOCAL_STATIC_LIBRARIES := libbase libcutils liblog
LOCAL_MODULE := simpleperf_unit_test
LOCAL_MODULE_TAGS := optional
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_NATIVE_TEST)
ifeq ($(HOST_OS),linux)
include $(CLEAR_VARS)
LOCAL_CLANG := true
LOCAL_CPPFLAGS := $(simpleperf_common_cppflags)
LOCAL_SRC_FILES := $(simpleperf_unit_test_src_files)
LOCAL_WHOLE_STATIC_LIBRARIES := libsimpleperf
LOCAL_STATIC_LIBRARIES := libbase libcutils liblog
LOCAL_MODULE := simpleperf_unit_test
LOCAL_MODULE_TAGS := optional
LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
include $(BUILD_HOST_NATIVE_TEST)
endif
......@@ -24,6 +24,17 @@
#include "event_type.h"
#include "perf_event.h"
static void 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");
}
class ListCommand : public Command {
public:
ListCommand()
......@@ -33,10 +44,6 @@ class ListCommand : public Command {
}
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) {
......@@ -53,15 +60,4 @@ bool ListCommand::Run(const std::vector<std::string>& args) {
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;
/*
* 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 <gtest/gtest.h>
#include <command.h>
TEST(cmd_list, smoke) {
Command* list_cmd = Command::FindCommandByName("list");
ASSERT_TRUE(list_cmd != nullptr);
ASSERT_TRUE(list_cmd->Run({}));
}
/*
* 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 <inttypes.h>
#include <stdio.h>
#include <chrono>
#include <string>
#include <vector>
#include <base/logging.h>
#include <base/strings.h>
#include "command.h"
#include "environment.h"
#include "event_attr.h"
#include "event_fd.h"
#include "event_type.h"
#include "perf_event.h"
#include "utils.h"
#include "workload.h"
static std::vector<std::string> default_measured_event_types{
"cpu-cycles", "stalled-cycles-frontend", "stalled-cycles-backend", "instructions",
"branch-instructions", "branch-misses", "task-clock", "context-switches", "page-faults",
};
class StatCommandImpl {
public:
StatCommandImpl() : verbose_mode_(false), system_wide_collection_(false) {
}
bool Run(const std::vector<std::string>& args);
private:
bool ParseOptions(const std::vector<std::string>& args, std::vector<std::string>* non_option_args);
bool AddMeasuredEventType(const std::string& event_type_name,
bool report_unsupported_types = true);
bool AddDefaultMeasuredEventTypes();
bool OpenEventFilesForCpus(const std::vector<int>& cpus);
bool OpenEventFilesForProcess(pid_t pid);
bool StartCounting();
bool StopCounting();
bool ReadCounters();
bool ShowCounters(std::chrono::steady_clock::duration counting_duration);
struct EventElem {
const EventType* const event_type;
std::vector<std::unique_ptr<EventFd>> event_fds;
std::vector<PerfCounter> event_counters;
PerfCounter sum_counter;
EventElem(const EventType* event_type) : event_type(event_type) {
}
};
std::vector<EventElem> measured_events_;
bool verbose_mode_;
bool system_wide_collection_;
};
bool StatCommandImpl::Run(const std::vector<std::string>& args) {
// 1. Parse options.
std::vector<std::string> workload_args;
if (!ParseOptions(args, &workload_args)) {
return false;
}
// 2. Add default measured event types.
if (measured_events_.empty()) {
if (!AddDefaultMeasuredEventTypes()) {
return false;
}
}
// 3. Create workload.
if (workload_args.empty()) {
workload_args = std::vector<std::string>({"sleep", "1"});
}
std::unique_ptr<Workload> workload = Workload::CreateWorkload(workload_args);
if (workload == nullptr) {
return false;
}
// 4. Open perf_event_files.
if (system_wide_collection_) {
std::vector<int> cpus = GetOnlineCpus();
if (cpus.empty() || !OpenEventFilesForCpus(cpus)) {
return false;
}
} else {
if (!OpenEventFilesForProcess(workload->GetWorkPid())) {
return false;
}
}
// 5. Count events while workload running.
auto start_time = std::chrono::steady_clock::now();
if (!StartCounting()) {
return false;
}
if (!workload->Start()) {
return false;
}
workload->WaitFinish();
if (!StopCounting()) {
return false;
}
auto end_time = std::chrono::steady_clock::now();
// 6. Read and print counters.
if (!ReadCounters()) {
return false;
}
if (!ShowCounters(end_time - start_time)) {
return false;
}
return true;
}
bool StatCommandImpl::ParseOptions(const std::vector<std::string>& args,
std::vector<std::string>* non_option_args) {
size_t i;
for (i = 0; i < args.size() && args[i].size() > 0 && args[i][0] == '-'; ++i) {
if (args[i] == "-a") {
system_wide_collection_ = true;
} else if (args[i] == "-e") {
if (i + 1 == args.size()) {
LOG(ERROR) << "No event list following -e option. Try `simpleperf help stat`";
return false;
}
++i;
std::vector<std::string> event_types = android::base::Split(args[i], ",");
for (auto& event_type : event_types) {
if (!AddMeasuredEventType(event_type)) {
return false;
}
}
} else if (args[i] == "--verbose") {
verbose_mode_ = true;
} else {
LOG(ERROR) << "Unknown option for stat command: " << args[i];
LOG(ERROR) << "Try `simpleperf help stat`";
return false;
}
}
if (non_option_args != nullptr) {
non_option_args->clear();
for (; i < args.size(); ++i) {
non_option_args->push_back(args[i]);
}
}
return true;
}
bool StatCommandImpl::AddMeasuredEventType(const std::string& event_type_name,
bool report_unsupported_types) {
const EventType* event_type = EventTypeFactory::FindEventTypeByName(event_type_name);
if (event_type == nullptr) {
LOG(ERROR) << "Unknown event_type: " << event_type_name;
LOG(ERROR) << "Try `simpleperf help list` to list all possible event type names";
return false;
}
if (!event_type->IsSupportedByKernel()) {
(report_unsupported_types ? LOG(ERROR) : LOG(DEBUG)) << "Event type " << event_type->name
<< " is not supported by the kernel";
return false;
}
measured_events_.push_back(EventElem(event_type));
return true;
}
bool StatCommandImpl::AddDefaultMeasuredEventTypes() {
for (auto& name : default_measured_event_types) {
// It is not an error when some event types in the default list are not supported by the kernel.
AddMeasuredEventType(name, false);
}
if (measured_events_.empty()) {
LOG(ERROR) << "Failed to add any supported default measured types";
return false;
}
return true;
}
bool StatCommandImpl::OpenEventFilesForCpus(const std::vector<int>& cpus) {
for (auto& elem : measured_events_) {
EventAttr attr = EventAttr::CreateDefaultAttrToMonitorEvent(*elem.event_type);
std::vector<std::unique_ptr<EventFd>> event_fds;
for (auto& cpu : cpus) {
auto event_fd = EventFd::OpenEventFileForCpu(attr, cpu);
if (event_fd != nullptr) {
event_fds.push_back(std::move(event_fd));
}
}
// As the online cpus can be enabled or disabled at runtime, we may not open perf_event_file
// for all cpus successfully. But we should open at least one cpu successfully for each event
// type.
if (event_fds.empty()) {
LOG(ERROR) << "failed to open perf_event_files for event_type " << elem.event_type->name
<< " on all cpus";
return false;
}
elem.event_fds = std::move(event_fds);
}
return true;
}
bool StatCommandImpl::OpenEventFilesForProcess(pid_t pid) {
for (auto& elem : measured_events_) {
EventAttr attr = EventAttr::CreateDefaultAttrToMonitorEvent(*elem.event_type);
std::vector<std::unique_ptr<EventFd>> event_fds;
auto event_fd = EventFd::OpenEventFileForProcess(attr, pid);
if (event_fd == nullptr) {
PLOG(ERROR) << "failed to open perf_event_file for event_type " << elem.event_type->name
<< " on pid " << pid;
return false;
}
event_fds.push_back(std::move(event_fd));
elem.event_fds = std::move(event_fds);
}
return true;
}
bool StatCommandImpl::StartCounting() {
for (auto& elem : measured_events_) {
for (auto& event_fd : elem.event_fds) {
if (!event_fd->EnableEvent()) {
return false;
}
}
}
return true;
}
bool StatCommandImpl::StopCounting() {
for (auto& elem : measured_events_) {
for (auto& event_fd : elem.event_fds) {
if (!event_fd->DisableEvent()) {
return false;
}
}
}
return true;
}
bool StatCommandImpl::ReadCounters() {
for (auto& elem : measured_events_) {
std::vector<PerfCounter> event_counters;
for (auto& event_fd : elem.event_fds) {
PerfCounter counter;
if (!event_fd->ReadCounter(&counter)) {
return false;
}
event_counters.push_back(counter);
}
PerfCounter sum_counter = event_counters.front();
for (size_t i = 1; i < event_counters.size(); ++i) {
sum_counter.value += event_counters[i].value;
sum_counter.time_enabled += event_counters[i].time_enabled;
sum_counter.time_running += event_counters[i].time_running;
}
elem.event_counters = event_counters;
elem.sum_counter = sum_counter;
}
return true;
}
bool StatCommandImpl::ShowCounters(std::chrono::steady_clock::duration counting_duration) {
printf("Performance counter statistics:\n\n");
for (auto& elem : measured_events_) {
std::string event_type_name = elem.event_type->name;
if (verbose_mode_) {
auto& event_fds = elem.event_fds;
auto& counters = elem.event_counters;
for (size_t i = 0; i < elem.event_fds.size(); ++i) {
printf("%s: value %'" PRId64 ", time_enabled %" PRId64 ", time_disabled %" PRId64
", id %" PRId64 "\n",
event_fds[i]->Name().c_str(), counters[i].value, counters[i].time_enabled,
counters[i].time_running, counters[i].id);
}
}
auto& counter = elem.sum_counter;
bool scaled = false;
int64_t scaled_count = counter.value;
if (counter.time_running < counter.time_enabled) {
if (counter.time_running == 0) {
scaled_count = 0;
} else {
scaled = true;
scaled_count = static_cast<int64_t>(static_cast<double>(counter.value) *
counter.time_enabled / counter.time_running);
}
}
printf("%'30" PRId64 "%s %s\n", scaled_count, scaled ? "(scaled)" : " ",
event_type_name.c_str());
}
printf("\n");
printf("Total test time: %lf seconds.\n",
std::chrono::duration_cast<std::chrono::duration<double>>(counting_duration).count());
return true;
}
class StatCommand : public Command {
public:
StatCommand()
: Command("stat", "gather performance counter information",
"Usage: simpleperf stat [options] [command [command-args]]\n"
" Gather performance counter information of running [command]. If [command]\n"
" is not specified, sleep 1 is used instead.\n\n"
" -a Collect system-wide information.\n"
" -e event1,event2,... Select the event list to count. Use `simpleperf list`\n"
" to find all possible event names.\n"
" --verbose Show result in verbose mode.\n"
" --help Print this help information.\n") {
}
bool Run(const std::vector<std::string>& args) override {
for (auto& arg : args) {
if (arg == "--help") {
printf("%s\n", LongHelpString().c_str());
return true;
}
}
StatCommandImpl impl;
return impl.Run(args);
}
};
StatCommand stat_command;
/*
* 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 <gtest/gtest.h>
#include <chrono>
#include <command.h>
class StatCommandTest : public ::testing::Test {
protected:
virtual void SetUp() {
stat_cmd = Command::FindCommandByName("stat");
ASSERT_TRUE(stat_cmd != nullptr);
}
protected:
Command* stat_cmd;
};
TEST_F(StatCommandTest, no_options) {
ASSERT_TRUE(stat_cmd->Run({}));
}
TEST_F(StatCommandTest, event_option) {
ASSERT_TRUE(stat_cmd->Run({"-e", "cpu-clock,task-clock"}));
}
TEST_F(StatCommandTest, system_wide_option) {
ASSERT_TRUE(stat_cmd->Run({"-a"}));
}
TEST_F(StatCommandTest, verbose_option) {
ASSERT_TRUE(stat_cmd->Run({"--verbose"}));
}
TEST_F(StatCommandTest, help_option) {
ASSERT_TRUE(stat_cmd->Run({"--help"}));
}
......@@ -48,3 +48,12 @@ const std::vector<Command*>& Command::GetAllCommands() {
void Command::RegisterCommand(Command* cmd) {
Commands().push_back(cmd);
}
void Command::UnRegisterCommand(Command* cmd) {
for (auto it = Commands().begin(); it != Commands().end(); ++it) {
if (*it == cmd) {
Commands().erase(it);
break;
}
}
}
......@@ -31,6 +31,7 @@ class Command {
}
virtual ~Command() {
UnRegisterCommand(this);
}
const std::string& Name() const {
......@@ -56,6 +57,7 @@ class Command {
const std::string long_help_string_;
static void RegisterCommand(Command* cmd);
static void UnRegisterCommand(Command* cmd);
DISALLOW_COPY_AND_ASSIGN(Command);
};
......
/*
* 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 <gtest/gtest.h>
#include <command.h>
class MockCommand : public Command {
public:
MockCommand(const std::string& name) : Command(name, name + "_short_help", name + "_long_help") {
}
bool Run(const std::vector<std::string>&) override {
return true;
}
};
TEST(command, FindCommandByName) {
ASSERT_EQ(Command::FindCommandByName("mock1"), nullptr);
{
MockCommand mock1("mock1");
ASSERT_EQ(Command::FindCommandByName("mock1"), &mock1);
}
ASSERT_EQ(Command::FindCommandByName("mock1"), nullptr);
}
TEST(command, GetAllCommands) {
size_t command_count = Command::GetAllCommands().size();
{
MockCommand mock1("mock1");
ASSERT_EQ(command_count + 1, Command::GetAllCommands().size());
}
ASSERT_EQ(command_count, Command::GetAllCommands().size());
}
/*
* 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 "environment.h"
#include <stdlib.h>
#include <vector>
#include <base/logging.h>
#include "utils.h"
std::vector<int> GetOnlineCpus() {
std::vector<int> result;
FILE* fp = fopen("/sys/devices/system/cpu/online", "re");
if (fp == nullptr) {
PLOG(ERROR) << "can't open online cpu information";
return result;
}
LineReader reader(fp);
char* line;
if ((line = reader.ReadLine()) != nullptr) {
result = GetOnlineCpusFromString(line);
}
CHECK(!result.empty()) << "can't get online cpu information";
return result;
}
std::vector<int> GetOnlineCpusFromString(const std::string& s) {
std::vector<int> result;
bool have_dash = false;
const char* p = s.c_str();
char* endp;
long cpu;
// Parse line like: 0,1-3, 5, 7-8
while ((cpu = strtol(p, &endp, 10)) != 0 || endp != p) {
if (have_dash && result.size() > 0) {
for (int t = result.back() + 1; t < cpu; ++t) {
result.push_back(t);
}
}
have_dash = false;
result.push_back(cpu);
p = endp;
while (!isdigit(*p) && *p != '\0') {
if (*p == '-') {
have_dash = true;
}
++p;
}
}
return result;
}
/*
* 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_ENVIRONMENT_H_
#define SIMPLE_PERF_ENVIRONMENT_H_
#include <string>
#include <vector>
std::vector<int> GetOnlineCpus();
std::vector<int> GetOnlineCpusFromString(const std::string& s);
#endif // SIMPLE_PERF_ENVIRONMENT_H_
/*
* 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 <gtest/gtest.h>
#include <environment.h>
TEST(environment, GetOnlineCpusFromString) {
ASSERT_EQ(GetOnlineCpusFromString(""), std::vector<int>());
ASSERT_EQ(GetOnlineCpusFromString("0-2"), std::vector<int>({0, 1, 2}));
ASSERT_EQ(GetOnlineCpusFromString("0,2-3"), std::vector<int>({0, 2, 3}));
}
......@@ -66,9 +66,12 @@ EventAttr EventAttr::CreateDefaultAttrToMonitorEvent(const EventType& event_type
attr.config = event_type.config;
attr.mmap = 1;
attr.comm = 1;
// Changing read_format affects the layout of the data read from perf_event_file, namely
// PerfCounter in event_fd.h.
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;
attr.disabled = 1;
return EventAttr(attr);
}
......
......@@ -94,3 +94,12 @@ bool EventFd::DisableEvent() {
}
return true;
}
bool EventFd::ReadCounter(PerfCounter* counter) {
CHECK(counter != nullptr);
if (!ReadNBytesFromFile(perf_event_fd_, counter, sizeof(*counter))) {
PLOG(ERROR) << "ReadCounter from " << Name() << " failed";
return false;
}
return true;
}
......@@ -24,8 +24,17 @@
#include <base/macros.h>
#include "perf_event.h"
class EventAttr;
struct PerfCounter {
uint64_t value; // The value of the event specified by the perf_event_file.
uint64_t time_enabled; // The enabled time.
uint64_t time_running; // The running time.
uint64_t id; // The id of the perf_event_file.
};
// EventFd represents an opened perf_event_file.
class EventFd {
public:
......@@ -44,6 +53,8 @@ class EventFd {
// It tells the kernel to stop counting and recording events specified by this file.
bool DisableEvent();
bool ReadCounter(PerfCounter* counter);
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) {
......
/*
* 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 <gtest/gtest.h>
#include <base/logging.h>
int main(int argc, char** argv) {
InitLogging(argv, android::base::StderrLogger);
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
......@@ -39,12 +39,12 @@ int main(int argc, char** argv) {
LOG(ERROR) << "malformed command line: unknown command " << args[0];
return 1;
}
std::string command_name = args[0];
args.erase(args.begin());
LOG(DEBUG) << "command '" << command_name << "' starts running";
bool result = command->Run(args);
if (result == true) {
LOG(DEBUG) << "run command " << args[0] << " successfully";
} else {
LOG(DEBUG) << "run command " << args[0] << "unsuccessfully";
}
LOG(DEBUG) << "command '" << command_name << "' "
<< (result ? "finished successfully" : "failed");
return result ? 0 : 1;
}
......@@ -16,8 +16,12 @@
#include "utils.h"
#include <errno.h>
#include <stdarg.h>
#include <stdio.h>
#include <unistd.h>
#include <base/logging.h>
void PrintIndented(size_t indent, const char* fmt, ...) {
va_list ap;
......@@ -26,3 +30,18 @@ void PrintIndented(size_t indent, const char* fmt, ...) {
vprintf(fmt, ap);
va_end(ap);
}
bool ReadNBytesFromFile(int fd, void* buf, size_t nbytes) {
char* p = reinterpret_cast<char*>(buf);
size_t bytes_left = nbytes;
while (bytes_left > 0) {
ssize_t nread = TEMP_FAILURE_RETRY(read(fd, p, bytes_left));
if (nread <= 0) {
return false;
} else {
p += nread;
bytes_left -= nread;
}
}
return true;
}
......@@ -18,7 +18,39 @@
#define SIMPLE_PERF_UTILS_H_
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string>
void PrintIndented(size_t indent, const char* fmt, ...);
class LineReader {
public:
LineReader(FILE* fp) : fp_(fp), buf_(nullptr), bufsize_(0) {
}
~LineReader() {
free(buf_);
fclose(fp_);
}
char* ReadLine() {
if (getline(&buf_, &bufsize_, fp_) != -1) {
return buf_;
}
return nullptr;
}
size_t MaxLineSize() {
return bufsize_;
}
private:
FILE* fp_;
char* buf_;
size_t bufsize_;
};
bool ReadNBytesFromFile(int fd, void* buf, size_t nbytes);
#endif // SIMPLE_PERF_UTILS_H_
/*
* 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 "workload.h"
#include <errno.h>
#include <fcntl.h>
#include <sys/wait.h>
#include <unistd.h>
#include <base/logging.h>
std::unique_ptr<Workload> Workload::CreateWorkload(const std::vector<std::string>& args) {
std::unique_ptr<Workload> workload(new Workload(args));
if (workload != nullptr && workload->CreateNewProcess()) {
return workload;
}
return nullptr;
}
static void ChildProcessFn(std::vector<std::string>& args, int start_signal_fd, int exec_child_fd);
bool Workload::CreateNewProcess() {
CHECK_EQ(work_state_, NotYetCreateNewProcess);
int start_signal_pipe[2];
if (pipe2(start_signal_pipe, O_CLOEXEC) != 0) {
PLOG(ERROR) << "pipe2() failed";
return false;
}
int exec_child_pipe[2];
if (pipe2(exec_child_pipe, O_CLOEXEC) != 0) {
PLOG(ERROR) << "pipe2() failed";
close(start_signal_pipe[0]);
close(start_signal_pipe[1]);
return false;
}
pid_t pid = fork();
if (pid == -1) {
PLOG(ERROR) << "fork() failed";
close(start_signal_pipe[0]);
close(start_signal_pipe[1]);
close(exec_child_pipe[0]);
close(exec_child_pipe[1]);
return false;
} else if (pid == 0) {
// In child process.
close(start_signal_pipe[1]);
close(exec_child_pipe[0]);
ChildProcessFn(args_, start_signal_pipe[0], exec_child_pipe[1]);
}
// In parent process.
close(start_signal_pipe[0]);
close(exec_child_pipe[1]);
start_signal_fd_ = start_signal_pipe[1];
exec_child_fd_ = exec_child_pipe[0];
work_pid_ = pid;
work_state_ = NotYetStartNewProcess;
return true;
}
static void ChildProcessFn(std::vector<std::string>& args, int start_signal_fd, int exec_child_fd) {
std::vector<char*> argv(args.size() + 1);
for (size_t i = 0; i < args.size(); ++i) {
argv[i] = &args[i][0];
}
argv[args.size()] = nullptr;
char start_signal = 0;
ssize_t nread = TEMP_FAILURE_RETRY(read(start_signal_fd, &start_signal, 1));
if (nread == 1 && start_signal == 1) {
close(start_signal_fd);
execvp(argv[0], argv.data());
// If execvp() succeed, we will not arrive here. But if it failed, we need to
// report the failure to the parent process by writing 1 to exec_child_fd.
int saved_errno = errno;
char exec_child_failed = 1;
TEMP_FAILURE_RETRY(write(exec_child_fd, &exec_child_failed, 1));
close(exec_child_fd);
errno = saved_errno;
PLOG(FATAL) << "execvp() failed";
} else {
PLOG(FATAL) << "child process failed to receive start_signal";
}
}
bool Workload::Start() {
CHECK_EQ(work_state_, NotYetStartNewProcess);
char start_signal = 1;
ssize_t nwrite = TEMP_FAILURE_RETRY(write(start_signal_fd_, &start_signal, 1));
if (nwrite != 1) {
PLOG(ERROR) << "write start signal failed";
return false;
}
char exec_child_failed;
ssize_t nread = TEMP_FAILURE_RETRY(read(exec_child_fd_, &exec_child_failed, 1));
if (nread != 0) {
LOG(ERROR) << "exec child failed";
return false;
}
work_state_ = Started;
return true;
}
bool Workload::IsFinished() {
if (work_state_ == Started) {
WaitChildProcess(true);
}
return work_state_ == Finished;
}
void Workload::WaitFinish() {
CHECK(work_state_ == Started || work_state_ == Finished);
if (work_state_ == Started) {
WaitChildProcess(false);
}
}
void Workload::WaitChildProcess(bool no_hang) {
int status;
pid_t result = TEMP_FAILURE_RETRY(waitpid(work_pid_, &status, (no_hang ? WNOHANG : 0)));
if (result == work_pid_) {
work_state_ = Finished;
if (WIFSIGNALED(status)) {
LOG(ERROR) << "work process was terminated by signal " << strsignal(WTERMSIG(status));
} else if (WIFEXITED(status) && WEXITSTATUS(status) != 0) {
LOG(ERROR) << "work process exited with exit code " << WEXITSTATUS(status);
}
} else if (result == -1) {
PLOG(FATAL) << "waitpid() failed";
}
}
/*
* 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_WORKLOAD_H_
#define SIMPLE_PERF_WORKLOAD_H_
#include <sys/types.h>
#include <chrono>
#include <string>
#include <vector>
#include <base/macros.h>
class Workload {
private:
enum WorkState {
NotYetCreateNewProcess,
NotYetStartNewProcess,
Started,
Finished,
};
public:
static std::unique_ptr<Workload> CreateWorkload(const std::vector<std::string>& args);
~Workload() {
if (start_signal_fd_ != -1) {
close(start_signal_fd_);
}
if (exec_child_fd_ != -1) {
close(exec_child_fd_);
}
}
bool Start();
bool IsFinished();
void WaitFinish();
pid_t GetWorkPid() {
return work_pid_;
}
private:
Workload(const std::vector<std::string>& args)
: work_state_(NotYetCreateNewProcess),
args_(args),
work_pid_(-1),
start_signal_fd_(-1),
exec_child_fd_(-1) {
}
bool CreateNewProcess();
void WaitChildProcess(bool no_hang);
WorkState work_state_;
std::vector<std::string> args_;
pid_t work_pid_;
int start_signal_fd_; // The parent process writes 1 to start workload in the child process.
int exec_child_fd_; // The child process writes 1 to notify that execvp() failed.
DISALLOW_COPY_AND_ASSIGN(Workload);
};
#endif // SIMPLE_PERF_WORKLOAD_H_
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment