summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorElliott Hughes <enh@google.com>2015-04-07 20:10:14 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2015-04-07 20:10:15 +0000
commit46b4f7d4288acfbf4c8a4aabcd70a47fa92744d9 (patch)
tree40a8295c7009d034e30c7c997379c357c2958468
parent01869e7a7c7afb0074c0caa2bda34f38aa83ed2a (diff)
parent7e2f4e9d384d501cf86118ebac4b8de2b86eac53 (diff)
downloadextras-46b4f7d4288acfbf4c8a4aabcd70a47fa92744d9.tar.gz
Merge "Perf profile collection daemon."android-wear-5.1.1_r1android-wear-5.1.0_r1
-rw-r--r--perfprofd/Android.mk75
-rw-r--r--perfprofd/perf_data_converter.cc130
-rw-r--r--perfprofd/perf_data_converter.h15
-rw-r--r--perfprofd/perf_profile.proto92
-rw-r--r--perfprofd/perfprofd.conf20
-rw-r--r--perfprofd/perfprofdcore.cc747
-rw-r--r--perfprofd/perfprofdcore.h56
-rw-r--r--perfprofd/perfprofdmain.cc23
-rw-r--r--perfprofd/perfprofdutils.cc54
-rw-r--r--perfprofd/perfprofdutils.h36
-rw-r--r--perfprofd/quipper/address_mapper.cc217
-rw-r--r--perfprofd/quipper/address_mapper.h128
-rw-r--r--perfprofd/quipper/base/basictypes.h58
-rw-r--r--perfprofd/quipper/base/compiler_specific.h208
-rw-r--r--perfprofd/quipper/base/logging.cc110
-rw-r--r--perfprofd/quipper/base/logging.h671
-rw-r--r--perfprofd/quipper/base/macros.h257
-rw-r--r--perfprofd/quipper/base/port.h48
-rw-r--r--perfprofd/quipper/build/build_config.h159
-rw-r--r--perfprofd/quipper/kernel-headers/tools/perf/perf.h196
-rw-r--r--perfprofd/quipper/kernel-headers/tools/perf/util/build-id.h25
-rw-r--r--perfprofd/quipper/kernel-headers/tools/perf/util/event.h204
-rw-r--r--perfprofd/quipper/kernel-headers/tools/perf/util/header.h121
-rw-r--r--perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/bitops.h41
-rw-r--r--perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/kernel/kernel.h79
-rw-r--r--perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/types.h43
-rw-r--r--perfprofd/quipper/kernel-headers/tools/perf/util/target.h52
-rw-r--r--perfprofd/quipper/kernel-headers/tools/perf/util/types.h38
-rw-r--r--perfprofd/quipper/original-kernel-headers/tools/perf/perf.h236
-rw-r--r--perfprofd/quipper/original-kernel-headers/tools/perf/util/build-id.h19
-rw-r--r--perfprofd/quipper/original-kernel-headers/tools/perf/util/event.h263
-rw-r--r--perfprofd/quipper/original-kernel-headers/tools/perf/util/header.h159
-rw-r--r--perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/bitops.h158
-rw-r--r--perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/kernel/kernel.h134
-rw-r--r--perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/types.h29
-rw-r--r--perfprofd/quipper/original-kernel-headers/tools/perf/util/target.h65
-rw-r--r--perfprofd/quipper/original-kernel-headers/tools/perf/util/types.h24
-rw-r--r--perfprofd/quipper/perf_internals.h64
-rw-r--r--perfprofd/quipper/perf_parser.cc576
-rw-r--r--perfprofd/quipper/perf_parser.h249
-rw-r--r--perfprofd/quipper/perf_reader.cc1645
-rw-r--r--perfprofd/quipper/perf_reader.h296
-rw-r--r--perfprofd/quipper/perf_utils.cc180
-rw-r--r--perfprofd/quipper/perf_utils.h112
-rw-r--r--perfprofd/quipper/quipper_string.h13
-rw-r--r--perfprofd/quipper/quipper_test.h10
-rw-r--r--perfprofd/tests/Android.mk49
-rw-r--r--perfprofd/tests/README.txt58
-rw-r--r--perfprofd/tests/canned.perf.databin0 -> 1366208 bytes
-rw-r--r--perfprofd/tests/perfprofd_test.cc639
-rw-r--r--perfprofd/tests/perfprofdmockutils.cc105
-rw-r--r--perfprofd/tests/perfprofdmockutils.h31
52 files changed, 9017 insertions, 0 deletions
diff --git a/perfprofd/Android.mk b/perfprofd/Android.mk
new file mode 100644
index 00000000..4ae70317
--- /dev/null
+++ b/perfprofd/Android.mk
@@ -0,0 +1,75 @@
+LOCAL_PATH:= $(call my-dir)
+
+perfprofd_cppflags := \
+ -Wall \
+ -Wno-sign-compare \
+ -Wno-unused-parameter \
+ -Werror \
+ -std=gnu++11 \
+
+#
+# Static library containing guts of AWP daemon.
+#
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_CPP_EXTENSION := cc
+LOCAL_MODULE := libperfprofdcore
+LOCAL_MODULE_CLASS := STATIC_LIBRARIES
+proto_header_dir := $(call local-generated-sources-dir)/proto/$(LOCAL_PATH)
+LOCAL_C_INCLUDES += $(proto_header_dir) $(LOCAL_PATH)/quipper/kernel-headers
+LOCAL_EXPORT_C_INCLUDE_DIRS += $(proto_header_dir)
+LOCAL_SRC_FILES := \
+ perf_profile.proto \
+ quipper/perf_utils.cc \
+ quipper/base/logging.cc \
+ quipper/address_mapper.cc \
+ quipper/perf_reader.cc \
+ quipper/perf_parser.cc \
+ perf_data_converter.cc \
+ perfprofdcore.cc \
+
+LOCAL_CPPFLAGS += $(perfprofd_cppflags)
+include $(BUILD_STATIC_LIBRARY)
+
+#
+# Static library with primary utilities layer (called by perfprofd core)
+#
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_CPP_EXTENSION := cc
+LOCAL_CXX_STL := libc++
+LOCAL_MODULE := libperfprofdutils
+LOCAL_CPPFLAGS += $(perfprofd_cppflags)
+LOCAL_SRC_FILES := perfprofdutils.cc
+include $(BUILD_STATIC_LIBRARY)
+
+#
+# Main daemon
+#
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_CPP_EXTENSION := cc
+LOCAL_CXX_STL := libc++
+LOCAL_SRC_FILES := perfprofdmain.cc
+LOCAL_STATIC_LIBRARIES := libperfprofdcore libperfprofdutils
+LOCAL_SHARED_LIBRARIES := liblog libprotobuf-cpp-full
+LOCAL_SYSTEM_SHARED_LIBRARIES := libc libstdc++
+LOCAL_CPPFLAGS += $(perfprofd_cppflags)
+LOCAL_CFLAGS := -Wall -Werror -std=gnu++11
+LOCAL_MODULE := perfprofd
+LOCAL_SHARED_LIBRARIES += libcutils
+include $(BUILD_EXECUTABLE)
+
+#
+# Config file (perfprofd.conf)
+#
+include $(CLEAR_VARS)
+LOCAL_MODULE := perfprofd.conf
+LOCAL_SRC_FILES := perfprofd.conf
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_PATH := $(TARGET_ROOT_OUT)/system/etc
+include $(BUILD_PREBUILT)
+
+# Clean temp vars
+perfprofd_cppflags :=
+proto_header_dir :=
diff --git a/perfprofd/perf_data_converter.cc b/perfprofd/perf_data_converter.cc
new file mode 100644
index 00000000..9d997538
--- /dev/null
+++ b/perfprofd/perf_data_converter.cc
@@ -0,0 +1,130 @@
+
+#include "perf_data_converter.h"
+#include "quipper/perf_parser.h"
+#include <map>
+
+using std::map;
+
+namespace wireless_android_logging_awp {
+
+struct RangeTarget {
+ RangeTarget(uint64 start, uint64 end, uint64 to)
+ : start(start), end(end), to(to) {}
+
+ bool operator<(const RangeTarget &r) const {
+ if (start != r.start) {
+ return start < r.start;
+ } else if (end != r.end) {
+ return end < r.end;
+ } else {
+ return to < r.to;
+ }
+ }
+ uint64 start;
+ uint64 end;
+ uint64 to;
+};
+
+struct BinaryProfile {
+ map<uint64, uint64> address_count_map;
+ map<RangeTarget, uint64> range_count_map;
+};
+
+wireless_android_play_playlog::AndroidPerfProfile
+RawPerfDataToAndroidPerfProfile(const string &perf_file) {
+ wireless_android_play_playlog::AndroidPerfProfile ret;
+ quipper::PerfParser parser;
+ if (!parser.ReadFile(perf_file) || !parser.ParseRawEvents()) {
+ return ret;
+ }
+
+ typedef map<string, BinaryProfile> ModuleProfileMap;
+ typedef map<string, ModuleProfileMap> ProgramProfileMap;
+ ProgramProfileMap name_profile_map;
+ uint64 total_samples = 0;
+ for (const auto &event : parser.parsed_events()) {
+ if (!event.raw_event ||
+ event.raw_event->header.type != PERF_RECORD_SAMPLE) {
+ continue;
+ }
+ string dso_name = event.dso_and_offset.dso_name();
+ string program_name;
+ if (dso_name == "[kernel.kallsyms]_text") {
+ program_name = "kernel";
+ dso_name = "[kernel.kallsyms]";
+ } else if (event.command() == "") {
+ program_name = "unknown_program";
+ } else {
+ program_name = event.command();
+ }
+ name_profile_map[program_name][dso_name].address_count_map[
+ event.dso_and_offset.offset()]++;
+ total_samples++;
+ for (size_t i = 1; i < event.branch_stack.size(); i++) {
+ if (dso_name == event.branch_stack[i - 1].to.dso_name()) {
+ uint64 start = event.branch_stack[i].to.offset();
+ uint64 end = event.branch_stack[i - 1].from.offset();
+ uint64 to = event.branch_stack[i - 1].to.offset();
+ // The interval between two taken branches should not be too large.
+ if (end < start || end - start > (1 << 20)) {
+ LOG(WARNING) << "Bogus LBR data: " << start << "->" << end;
+ continue;
+ }
+ name_profile_map[program_name][dso_name].range_count_map[
+ RangeTarget(start, end, to)]++;
+ }
+ }
+ }
+
+ map<string, int> name_id_map;
+ for (const auto &program_profile : name_profile_map) {
+ for (const auto &module_profile : program_profile.second) {
+ name_id_map[module_profile.first] = 0;
+ }
+ }
+ int current_index = 0;
+ for (auto iter = name_id_map.begin(); iter != name_id_map.end(); ++iter) {
+ iter->second = current_index++;
+ }
+
+ map<string, string> name_buildid_map;
+ parser.GetFilenamesToBuildIDs(&name_buildid_map);
+ ret.set_total_samples(total_samples);
+ for (const auto &name_id : name_id_map) {
+ auto load_module = ret.add_load_modules();
+ load_module->set_name(name_id.first);
+ auto nbmi = name_buildid_map.find(name_id.first);
+ if (nbmi != name_buildid_map.end()) {
+ const std::string &build_id = nbmi->second;
+ if (build_id.size() == 40 && build_id.substr(32) == "00000000") {
+ load_module->set_build_id(build_id.substr(0, 32));
+ } else {
+ load_module->set_build_id(build_id);
+ }
+ }
+ }
+ for (const auto &program_profile : name_profile_map) {
+ auto program = ret.add_programs();
+ program->set_name(program_profile.first);
+ for (const auto &module_profile : program_profile.second) {
+ int32 module_id = name_id_map[module_profile.first];
+ auto module = program->add_modules();
+ module->set_load_module_id(module_id);
+ for (const auto &addr_count : module_profile.second.address_count_map) {
+ auto address_samples = module->add_address_samples();
+ address_samples->add_address(addr_count.first);
+ address_samples->set_count(addr_count.second);
+ }
+ for (const auto &range_count : module_profile.second.range_count_map) {
+ auto range_samples = module->add_range_samples();
+ range_samples->set_start(range_count.first.start);
+ range_samples->set_end(range_count.first.end);
+ range_samples->set_to(range_count.first.to);
+ range_samples->set_count(range_count.second);
+ }
+ }
+ }
+ return ret;
+}
+
+} // namespace wireless_android_logging_awp
diff --git a/perfprofd/perf_data_converter.h b/perfprofd/perf_data_converter.h
new file mode 100644
index 00000000..fdbde009
--- /dev/null
+++ b/perfprofd/perf_data_converter.h
@@ -0,0 +1,15 @@
+#ifndef WIRELESS_ANDROID_LOGGING_AWP_PERF_DATA_CONVERTER_H_
+#define WIRELESS_ANDROID_LOGGING_AWP_PERF_DATA_CONVERTER_H_
+
+#include "perf_profile.pb.h"
+
+#include <string>
+
+namespace wireless_android_logging_awp {
+
+wireless_android_play_playlog::AndroidPerfProfile
+RawPerfDataToAndroidPerfProfile(const std::string &perf_file);
+
+} // namespace wireless_android_logging_awp
+
+#endif // WIRELESS_ANDROID_LOGGING_AWP_PERF_DATA_CONVERTER_H_
diff --git a/perfprofd/perf_profile.proto b/perfprofd/perf_profile.proto
new file mode 100644
index 00000000..0c3da011
--- /dev/null
+++ b/perfprofd/perf_profile.proto
@@ -0,0 +1,92 @@
+
+syntax = "proto2";
+
+option java_package = "com.google.common.logging";
+
+package wireless_android_play_playlog;
+
+// An entry of the map from a stack of addresses to count.
+// Address here is the offset of the instruction address to the load address
+// of the load_module.
+message AddressSample {
+ // List of addresses that represents a call stack.
+ // address[0] is the leaf of the call stack.
+ repeated uint64 address = 1;
+
+ // List of load_module_ids that represents a call stack.
+ // load_module_id[0] is the leaf of the call stack.
+ // This field can be set as empty if all frame share the same load_module_id
+ // with LoadModuleSamples.load_module_id.
+ repeated int32 load_module_id = 2;
+
+ // Total count that the address/address_range is sampled.
+ optional int64 count = 3;
+};
+
+// An entry of the map from address_range to count.
+// [start, end] represents the range of addresses, end->to represents the
+// taken branch that ends the range.
+message RangeSample {
+ // Start instruction address of a range.
+ optional uint64 start = 1;
+
+ // If "end" and "to" is not provided, "start" represents a single instruction.
+ optional uint64 end = 2;
+ optional uint64 to = 3;
+
+ // Total count that the address/address_range is sampled.
+ optional int64 count = 4;
+};
+
+// A load module.
+message LoadModule {
+ // Name of the load_module.
+ optional string name = 1;
+
+ // LoadModule's linker build_id.
+ optional string build_id = 2;
+}
+
+// All samples for a load_module.
+message LoadModuleSamples {
+ optional int32 load_module_id = 1;
+
+ // Map from a stack of addresses to count.
+ repeated AddressSample address_samples = 2;
+
+ // Map from a range triplet (start, end, to) to count.
+ repeated RangeSample range_samples = 3;
+}
+
+// All samples for a program.
+message ProgramSamples {
+ // Name of the program.
+ optional string name = 1;
+
+ // Load module profiles.
+ repeated LoadModuleSamples modules = 2;
+}
+
+// A compressed representation of a perf profile, which contains samples from
+// multiple binaries.
+message AndroidPerfProfile {
+
+ // Type of the hardware event.
+ enum EventType {
+ CYCLE = 0;
+ BRANCH = 1;
+ }
+ // Hardware event used in profiling.
+ optional EventType event = 1;
+
+ // Total number of samples in this profile.
+ // This is the sum of counts of address_samples and range_samples in all
+ // load_module_samples.
+ optional int64 total_samples = 2;
+
+ // Samples for all profiled programs.
+ repeated ProgramSamples programs = 3;
+
+ // List of all load modules.
+ repeated LoadModule load_modules = 4;
+} \ No newline at end of file
diff --git a/perfprofd/perfprofd.conf b/perfprofd/perfprofd.conf
new file mode 100644
index 00000000..482beea1
--- /dev/null
+++ b/perfprofd/perfprofd.conf
@@ -0,0 +1,20 @@
+#
+# Configuration file for perf profile collection daemon (perfprofd)
+#
+#------------------------------------------------------------------------
+#
+# Destination directory for profiles
+#
+destination_directory=/data/data/com.google.android.gms/files
+#
+# Sampling period (for perf -c option)
+#
+sampling_period=500000
+#
+# Average interval to wait between profile collection attempts (seconds)
+#
+collection_interval=86400
+#
+# Number of seconds of profile data to collect
+#
+sample_duration=3
diff --git a/perfprofd/perfprofdcore.cc b/perfprofd/perfprofdcore.cc
new file mode 100644
index 00000000..62cff186
--- /dev/null
+++ b/perfprofd/perfprofdcore.cc
@@ -0,0 +1,747 @@
+/*
+**
+** Copyright 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 <assert.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <time.h>
+#include <unistd.h>
+#include <string>
+#include <map>
+#include <cctype>
+
+#include <cutils/properties.h>
+
+#include "perfprofdcore.h"
+#include "perfprofdutils.h"
+#include "perf_data_converter.h"
+
+//
+// Perf profiling daemon -- collects system-wide profiles using
+//
+// /system/bin/perf record -a
+//
+// and encodes them so that they can be uploaded by a separate service.
+//
+
+//......................................................................
+
+//
+// Output file from 'perf record'. The linux 'perf' tool by default
+// creates a file with this name.
+//
+#define PERF_OUTPUT "perf.data"
+
+//
+// This enum holds the results of the "should we profile" configuration check.
+//
+typedef enum {
+
+ // All systems go for profile collection.
+ DO_COLLECT_PROFILE,
+
+ // The destination directory selected in the conf file doesn't exist. Most
+ // likely this is due to a missing or out-of-date version of the uploading
+ // service in GMS core.
+ DONT_PROFILE_MISSING_DESTINATION_DIR,
+
+ // Destination directory does not contain the semaphore file that
+ // the perf profile uploading service creates when it determines
+ // that the user has opted "in" for usage data collection. No
+ // semaphore -> no user approval -> no profiling.
+ DONT_PROFILE_MISSING_SEMAPHORE,
+
+ // No perf executable present
+ DONT_PROFILE_MISSING_PERF_EXECUTABLE,
+
+ // We're running in the emulator, perf won't be able to do much
+ DONT_PROFILE_RUNNING_IN_EMULATOR
+
+} CKPROFILE_RESULT;
+
+//
+// Are we running in the emulator? If so, stub out profile collection
+// Starts as uninitialized (-1), then set to 1 or 0 at init time.
+//
+static int running_in_emulator = -1;
+
+//
+// Is this a debug build ('userdebug' or 'eng')?
+// Starts as uninitialized (-1), then set to 1 or 0 at init time.
+//
+static int is_debug_build = -1;
+
+//
+// Random number generator seed (set at startup time).
+//
+static unsigned short random_seed[3];
+
+//
+// Config file path. May be overridden with -c command line option
+//
+static const char *config_file_path = "/system/etc/perfprofd.conf";
+
+//
+// Set by SIGHUP signal handler
+//
+volatile unsigned please_reread_config_file = 0;
+
+//
+// This table describes the config file syntax in terms of key/value pairs.
+// Values come in two flavors: strings, or unsigned integers. In the latter
+// case the reader sets allowable minimum/maximum for the setting.
+//
+class ConfigReader {
+
+ public:
+ ConfigReader();
+ ~ConfigReader();
+
+ // Ask for the current setting of a config item
+ unsigned getUnsignedValue(const char *key);
+ std::string getStringValue(const char *key);
+
+ // read the specified config file, applying any settings it contains
+ void readFile(const char *configFilePath);
+
+ private:
+ void addUnsignedEntry(const char *key,
+ unsigned default_value,
+ unsigned min_value,
+ unsigned max_value);
+ void addStringEntry(const char *key, const char *default_value);
+ void addDefaultEntries();
+ void parseLine(const char *key, const char *value, unsigned linecount);
+
+ typedef struct { unsigned minv, maxv; } values;
+ std::map<std::string, values> u_info;
+ std::map<std::string, unsigned> u_entries;
+ std::map<std::string, std::string> s_entries;
+ bool trace_config_read;
+};
+
+ConfigReader::ConfigReader()
+ : trace_config_read(false)
+{
+ addDefaultEntries();
+}
+
+ConfigReader::~ConfigReader()
+{
+}
+
+//
+// Populate the reader with the set of allowable entries
+//
+void ConfigReader::addDefaultEntries()
+{
+ // Average number of seconds between perf profile collections (if
+ // set to 100, then over time we want to see a perf profile
+ // collected every 100 seconds). The actual time within the interval
+ // for the collection is chosen randomly.
+ addUnsignedEntry("collection_interval", 901, 100, UINT32_MAX);
+
+ // Use the specified fixed seed for random number generation (unit
+ // testing)
+ addUnsignedEntry("use_fixed_seed", 0, 0, UINT32_MAX);
+
+ // For testing purposes, number of times to iterate through main
+ // loop. Value of zero indicates that we should loop forever.
+ addUnsignedEntry("main_loop_iterations", 0, 0, UINT32_MAX);
+
+ // Destination directory (where to write profiles). This location
+ // chosen since it is accessible to the uploader service.
+ addStringEntry("destination_directory",
+ "/data/data/com.google.android.gms/files");
+
+ // Path to 'perf' executable.
+ addStringEntry("perf_path", "/system/bin/perf");
+
+ // Desired sampling period (passed to perf -c option). Small
+ // sampling periods can perturb the collected profiles, so enforce
+ // min/max.
+ addUnsignedEntry("sampling_period", 500000, 5000, UINT32_MAX);
+
+ // Length of time to collect samples (number of seconds for 'perf
+ // record -a' run).
+ addUnsignedEntry("sample_duration", 3, 2, 600);
+
+ // If this parameter is non-zero it will cause perfprofd to
+ // exit immediately if the build type is not userdebug or eng.
+ // Currently defaults to 1 (true).
+ addUnsignedEntry("only_debug_build", 1, 0, 1);
+
+ // If set to 1, pass the -g option when invoking perf (requests
+ // stack traces as opposed to flat profile).
+ addUnsignedEntry("stack_profile", 0, 0, 1);
+
+ // For unit testing only: if set to 1, emit info messages on config
+ // file parsing.
+ addUnsignedEntry("trace_config_read", 0, 0, 1);
+}
+
+void ConfigReader::addUnsignedEntry(const char *key,
+ unsigned default_value,
+ unsigned min_value,
+ unsigned max_value)
+{
+ std::string ks(key);
+ if (u_entries.find(ks) != u_entries.end() ||
+ s_entries.find(ks) != s_entries.end()) {
+ W_ALOGE("internal error -- duplicate entry for key %s", key);
+ exit(9);
+ }
+ values vals;
+ vals.minv = min_value;
+ vals.maxv = max_value;
+ u_info[ks] = vals;
+ u_entries[ks] = default_value;
+}
+
+void ConfigReader::addStringEntry(const char *key, const char *default_value)
+{
+ std::string ks(key);
+ if (u_entries.find(ks) != u_entries.end() ||
+ s_entries.find(ks) != s_entries.end()) {
+ W_ALOGE("internal error -- duplicate entry for key %s", key);
+ exit(9);
+ }
+ if (! default_value) {
+ W_ALOGE("internal error -- bad default value for key %s", key);
+ exit(9);
+ }
+ s_entries[ks] = std::string(default_value);
+}
+
+unsigned ConfigReader::getUnsignedValue(const char *key)
+{
+ std::string ks(key);
+ auto it = u_entries.find(ks);
+ assert(it != u_entries.end());
+ return it->second;
+}
+
+std::string ConfigReader::getStringValue(const char *key)
+{
+ std::string ks(key);
+ auto it = s_entries.find(ks);
+ assert(it != s_entries.end());
+ return it->second;
+}
+
+//
+// Parse a key=value pair read from the config file. This will issue
+// warnings or errors to the system logs if the line can't be
+// interpreted properly.
+//
+void ConfigReader::parseLine(const char *key,
+ const char *value,
+ unsigned linecount)
+{
+ assert(key);
+ assert(value);
+
+ auto uit = u_entries.find(key);
+ if (uit != u_entries.end()) {
+ unsigned uvalue = 0;
+ if (! isdigit(value[0]) || sscanf(value, "%u", &uvalue) != 1) {
+ W_ALOGW("line %d: malformed unsigned value (ignored)", linecount);
+ } else {
+ values vals;
+ auto iit = u_info.find(key);
+ assert(iit != u_info.end());
+ vals = iit->second;
+ if (uvalue < vals.minv || uvalue > vals.maxv) {
+ W_ALOGW("line %d: specified value %u for '%s' "
+ "outside permitted range [%u %u] (ignored)",
+ linecount, uvalue, key, vals.minv, vals.maxv);
+ } else {
+ if (trace_config_read) {
+ W_ALOGI("option %s set to %u", key, uvalue);
+ }
+ uit->second = uvalue;
+ }
+ }
+ trace_config_read = (getUnsignedValue("trace_config_read") != 0);
+ return;
+ }
+
+ auto sit = s_entries.find(key);
+ if (sit != s_entries.end()) {
+ if (trace_config_read) {
+ W_ALOGI("option %s set to %s", key, value);
+ }
+ sit->second = std::string(value);
+ return;
+ }
+
+ W_ALOGW("line %d: unknown option '%s' ignored", linecount, key);
+}
+
+static bool isblank(const std::string &line)
+{
+ for (std::string::const_iterator it = line.begin(); it != line.end(); ++it)
+ {
+ if (! isspace(*it)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+void ConfigReader::readFile(const char *configFilePath)
+{
+ FILE *fp = fopen(configFilePath, "r");
+ if (!fp) {
+ W_ALOGE("unable to open configuration file %s", config_file_path);
+ return;
+ }
+
+ char *linebuf = NULL;
+ size_t line_length = 0;
+ for (unsigned linecount = 1;
+ getline(&linebuf, &line_length, fp) != -1;
+ ++linecount) {
+ char *eq = 0;
+ char *key, *value;
+
+ // comment line?
+ if (linebuf[0] == '#') {
+ continue;
+ }
+
+ // blank line?
+ if (isblank(linebuf)) {
+ continue;
+ }
+
+ // look for X=Y assignment
+ eq = strchr(linebuf, '=');
+ if (!eq) {
+ W_ALOGW("line %d: line malformed (no '=' found)", linecount);
+ continue;
+ }
+
+ *eq = '\0';
+ key = linebuf;
+ value = eq+1;
+ char *ln = strrchr(value, '\n');
+ if (ln) { *ln = '\0'; }
+
+ parseLine(key, value, linecount);
+ }
+ free(linebuf);
+ fclose(fp);
+}
+
+//
+// Parse command line args. Currently you can supply "-c P" to set
+// the path of the config file to P.
+//
+static void parse_args(int argc, char** argv)
+{
+ int ac;
+
+ for (ac = 1; ac < argc; ++ac) {
+ if (!strcmp(argv[ac], "-c")) {
+ if (ac >= argc-1) {
+ W_ALOGE("malformed command line: -c option requires argument)");
+ continue;
+ }
+ config_file_path = strdup(argv[ac+1]);
+ W_ALOGI("config file path set to %s", config_file_path);
+ ++ac;
+ } else {
+ W_ALOGE("malformed command line: unknown option or arg %s)", argv[ac]);
+ continue;
+ }
+ }
+}
+
+//
+// Convert a CKPROFILE_RESULT to a string
+//
+const char *ckprofile_result_to_string(CKPROFILE_RESULT result)
+{
+ switch(result) {
+ case DO_COLLECT_PROFILE:
+ return "DO_COLLECT_PROFILE";
+ case DONT_PROFILE_MISSING_DESTINATION_DIR:
+ return "missing destination directory";
+ case DONT_PROFILE_MISSING_SEMAPHORE:
+ return "missing semaphore file";
+ case DONT_PROFILE_MISSING_PERF_EXECUTABLE:
+ return "missing 'perf' executable";
+ case DONT_PROFILE_RUNNING_IN_EMULATOR:
+ return "running in emulator";
+ default: return "unknown";
+ }
+ return "notreached";
+}
+
+//
+// Convert a PROFILE_RESULT to a string
+//
+const char *profile_result_to_string(PROFILE_RESULT result)
+{
+ switch(result) {
+ case OK_PROFILE_COLLECTION:
+ return "profile collection succeeded";
+ case ERR_FORK_FAILED:
+ return "fork() system call failed";
+ case ERR_PERF_RECORD_FAILED:
+ return "perf record returned bad exit status";
+ case ERR_PERF_ENCODE_FAILED:
+ return "failure encoding perf.data to protobuf";
+ case ERR_OPEN_ENCODED_FILE_FAILED:
+ return "failed to open encoded perf file";
+ case ERR_WRITE_ENCODED_FILE_FAILED:
+ return "write to encoded perf file failed";
+ default: return "unknown";
+ }
+ return "notreached";
+}
+
+//
+// The daemon does a read of the main config file on startup, however
+// if the destination directory also contains a configf file, then we
+// read parameters from that as well. This provides a mechanism for
+// changing/controlling the behavior of the daemon via the settings
+// established in the uploader service (which may be easier to update
+// than the daemon).
+//
+static void read_aux_config(ConfigReader &config)
+{
+ std::string destConfig(config.getStringValue("destination_directory"));
+ destConfig += "/perfprofd.conf";
+ FILE *fp = fopen(destConfig.c_str(), "r");
+ if (fp) {
+ fclose(fp);
+ bool trace_config_read =
+ (config.getUnsignedValue("trace_config_read") != 0);
+ if (trace_config_read) {
+ W_ALOGI("reading auxiliary config file %s", destConfig.c_str());
+ }
+ config.readFile(destConfig.c_str());
+ }
+}
+
+//
+// Check to see whether we should perform a profile collection
+//
+static CKPROFILE_RESULT check_profiling_enabled(ConfigReader &config)
+{
+ //
+ // Profile collection in the emulator doesn't make sense
+ //
+ assert(running_in_emulator != -1);
+ if (running_in_emulator) {
+ return DONT_PROFILE_RUNNING_IN_EMULATOR;
+ }
+
+ //
+ // Check for the existence of the destination directory
+ //
+ std::string destdir = config.getStringValue("destination_directory");
+ DIR* dir = opendir(destdir.c_str());
+ if (!dir) {
+ W_ALOGW("unable to open destination directory %s: (%s)",
+ destdir.c_str(), strerror(errno));
+ return DONT_PROFILE_MISSING_DESTINATION_DIR;
+ }
+
+ // Reread aux config file -- it may have changed
+ read_aux_config(config);
+
+ // Check for existence of perf executable
+ std::string pp = config.getStringValue("perf_path");
+ if (access(pp.c_str(), R_OK|X_OK) == -1) {
+ W_ALOGW("unable to access/execute %s", pp.c_str());
+ closedir(dir);
+ return DONT_PROFILE_MISSING_PERF_EXECUTABLE;
+ }
+
+ // Check for existence of semaphore file
+ unsigned found = 0;
+ struct dirent* e;
+ while ((e = readdir(dir)) != 0) {
+ if (!strcmp(e->d_name, SEMAPHORE_FILENAME)) {
+ found = 1;
+ break;
+ }
+ }
+ closedir(dir);
+ if (!found) {
+ return DONT_PROFILE_MISSING_SEMAPHORE;
+ }
+
+ //
+ // We are good to go
+ //
+ return DO_COLLECT_PROFILE;
+}
+
+inline char* string_as_array(std::string* str) {
+ return str->empty() ? NULL : &*str->begin();
+}
+
+PROFILE_RESULT encode_to_proto(const std::string &data_file_path,
+ const std::string &encoded_file_path)
+{
+ //
+ // Open and read perf.data file
+ //
+ const wireless_android_play_playlog::AndroidPerfProfile &encodedProfile =
+ wireless_android_logging_awp::RawPerfDataToAndroidPerfProfile(data_file_path);
+
+ fprintf(stderr, "data file path is %s\n", data_file_path.c_str());
+ fprintf(stderr, "encoded file path is %s\n", encoded_file_path.c_str());
+
+ //
+ // Issue error if no samples
+ //
+ if (encodedProfile.programs().size() == 0) {
+ return ERR_PERF_ENCODE_FAILED;
+ }
+
+
+ //
+ // Serialize protobuf to array
+ //
+ int size = encodedProfile.ByteSize();
+ std::string data;
+ data.resize(size);
+ ::google::protobuf::uint8* dtarget =
+ reinterpret_cast<::google::protobuf::uint8*>(string_as_array(&data));
+ encodedProfile.SerializeWithCachedSizesToArray(dtarget);
+
+ //
+ // Open file and write encoded data to it
+ //
+ FILE *fp = fopen(encoded_file_path.c_str(), "w");
+ if (!fp) {
+ return ERR_OPEN_ENCODED_FILE_FAILED;
+ }
+ size_t fsiz = size;
+ if (fwrite(dtarget, fsiz, 1, fp) != 1) {
+ fclose(fp);
+ return ERR_WRITE_ENCODED_FILE_FAILED;
+ }
+ fclose(fp);
+
+ return OK_PROFILE_COLLECTION;
+}
+
+//
+// Collect a perf profile. Steps for this operation are:
+// - kick off 'perf record'
+// - read perf.data, convert to protocol buf
+//
+static PROFILE_RESULT collect_profile(ConfigReader &config)
+{
+ //
+ // Form perf.data file name, perf error output file name
+ //
+ std::string destdir = config.getStringValue("destination_directory");
+ std::string data_file_path(destdir);
+ data_file_path += "/";
+ data_file_path += PERF_OUTPUT;
+ std::string perf_stderr_path(destdir);
+ perf_stderr_path += "/perferr.txt";
+
+ //
+ // Remove any existing perf.data file -- if we don't do this, perf
+ // will rename the old file and we'll have extra cruft lying around.
+ //
+ struct stat statb;
+ if (stat(data_file_path.c_str(), &statb) == 0) { // if file exists...
+ if (unlink(data_file_path.c_str())) { // then try to remove
+ W_ALOGW("unable to unlink previous perf.data file");
+ }
+ }
+
+ //
+ // NB: it would probably be better to use explicit fork/exec with an
+ // alarm timeout in case of funny business. For now, call system().
+ //
+ std::string perf_path = config.getStringValue("perf_path");
+ unsigned duration = config.getUnsignedValue("sample_duration");
+ unsigned period = config.getUnsignedValue("sampling_period");
+ const char *gopt = (config.getUnsignedValue("stack_profile") != 0 ? "-g" : "");
+ char cmd[8192];
+ snprintf(cmd, 8192, "%s record %s -c %u -o %s -a -- sleep %d 1> %s 2>&1 ",
+ perf_path.c_str(), gopt, period, data_file_path.c_str(), duration,
+ perf_stderr_path.c_str());
+ int rc = system(cmd);
+ if (rc == -1) {
+ return ERR_FORK_FAILED;
+ }
+ if (rc != 0) {
+ return ERR_PERF_RECORD_FAILED;
+ }
+
+ //
+ // Read the resulting perf.data file, encode into protocol buffer, then write
+ // the result to a *.pdb file
+ //
+ std::string encoded_file_path(data_file_path);
+ encoded_file_path += ".encoded";
+ return encode_to_proto(data_file_path, encoded_file_path);
+}
+
+//
+// SIGHUP handler. Sets a flag to indicate that we should reread the
+// config file
+//
+static void sig_hup(int /* signum */)
+{
+ please_reread_config_file = 1;
+}
+
+//
+// Assuming that we want to collect a profile every N seconds,
+// randomly partition N into two sub-intervals.
+//
+static void determine_before_after(unsigned &sleep_before_collect,
+ unsigned &sleep_after_collect,
+ unsigned collection_interval)
+{
+ double frac = erand48(random_seed);
+ sleep_before_collect = (unsigned) (((double)collection_interval) * frac);
+ assert(sleep_before_collect <= collection_interval);
+ sleep_after_collect = collection_interval - sleep_before_collect;
+}
+
+//
+// Set random number generator seed
+//
+static void set_seed(ConfigReader &config)
+{
+ unsigned seed = 0;
+ unsigned use_fixed_seed = config.getUnsignedValue("use_fixed_seed");
+ if (use_fixed_seed) {
+ //
+ // Use fixed user-specified seed
+ //
+ seed = use_fixed_seed;
+ } else {
+ //
+ // Randomized seed
+ //
+ seed = arc4random();
+ }
+ W_ALOGI("random seed set to %u", seed);
+ // Distribute the 32-bit seed into the three 16-bit array
+ // elements. The specific values being written do not especially
+ // matter as long as we are setting them to something based on the seed.
+ random_seed[0] = seed & 0xffff;
+ random_seed[1] = (seed >> 16);
+ random_seed[2] = (random_seed[0] ^ random_seed[1]);
+}
+
+//
+// Initialization
+//
+static void init(ConfigReader &config)
+{
+ config.readFile(config_file_path);
+ set_seed(config);
+
+ char propBuf[PROPERTY_VALUE_MAX];
+ propBuf[0] = '\0';
+ property_get("ro.kernel.qemu", propBuf, "");
+ running_in_emulator = (propBuf[0] == '1');
+ property_get("ro.debuggable", propBuf, "");
+ is_debug_build = (propBuf[0] == '1');
+
+ signal(SIGHUP, sig_hup);
+}
+
+//
+// Main routine:
+// 1. parse cmd line args
+// 2. read config file
+// 3. loop: {
+// sleep for a while
+// perform a profile collection
+// }
+//
+int perfprofd_main(int argc, char** argv)
+{
+ ConfigReader config;
+
+ W_ALOGI("starting Android Wide Profiling daemon");
+
+ parse_args(argc, argv);
+ init(config);
+ read_aux_config(config);
+
+ // Early exit if we're not supposed to run on this build flavor
+ if (is_debug_build != 1 &&
+ config.getUnsignedValue("only_debug_build") == 1) {
+ W_ALOGI("early exit due to inappropriate build type");
+ return 0;
+ }
+
+ unsigned iterations = 0;
+ while(!config.getUnsignedValue("main_loop_iterations") ||
+ iterations < config.getUnsignedValue("main_loop_iterations")) {
+
+ // Figure out where in the collection interval we're going to actually
+ // run perf
+ unsigned sleep_before_collect = 0;
+ unsigned sleep_after_collect = 0;
+ determine_before_after(sleep_before_collect, sleep_after_collect,
+ config.getUnsignedValue("collection_interval"));
+ perfprofd_sleep(sleep_before_collect);
+
+ // Reread config file if someone sent a SIGHUP
+ if (please_reread_config_file) {
+ config.readFile(config_file_path);
+ please_reread_config_file = 0;
+ }
+
+ // Check for profiling enabled...
+ CKPROFILE_RESULT ckresult = check_profiling_enabled(config);
+ if (ckresult != DO_COLLECT_PROFILE) {
+ W_ALOGI("profile collection skipped (%s)",
+ ckprofile_result_to_string(ckresult));
+ } else {
+ // Kick off the profiling run...
+ W_ALOGI("initiating profile collection");
+ PROFILE_RESULT result = collect_profile(config);
+ if (result != OK_PROFILE_COLLECTION) {
+ W_ALOGI("profile collection failed (%s)",
+ profile_result_to_string(result));
+ } else {
+ W_ALOGI("profile collection complete");
+ }
+ }
+ perfprofd_sleep(sleep_after_collect);
+ iterations += 1;
+ }
+
+ W_ALOGI("finishing Android Wide Profiling daemon");
+ return 0;
+}
diff --git a/perfprofd/perfprofdcore.h b/perfprofd/perfprofdcore.h
new file mode 100644
index 00000000..1bff9ba4
--- /dev/null
+++ b/perfprofd/perfprofdcore.h
@@ -0,0 +1,56 @@
+/*
+**
+** Copyright 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.
+*/
+
+// Semaphore file that indicates that the user is opting in
+#define SEMAPHORE_FILENAME "perf_profile_collection_enabled.txt"
+
+// Main routine for perfprofd daemon
+extern int perfprofd_main(int argc, char **argv);
+
+//
+// This enumeration holds the results of what happened when on an
+// attempted perf profiling run.
+//
+typedef enum {
+
+ // Success
+ OK_PROFILE_COLLECTION,
+
+ // Fork system call failed (lo mem?)
+ ERR_FORK_FAILED,
+
+ // Perf ran but crashed or returned a bad exit status
+ ERR_PERF_RECORD_FAILED,
+
+ // The perf.data encoding process failed somehow
+ ERR_PERF_ENCODE_FAILED,
+
+ // We tried to open the output file perf.data.encoded but the open failed
+ ERR_OPEN_ENCODED_FILE_FAILED,
+
+ // Error while writing perf.data.encoded
+ ERR_WRITE_ENCODED_FILE_FAILED
+} PROFILE_RESULT;
+
+//
+// Given a full path to a perf.data file specified by "data_file_path",
+// read/summarize/encode the contents into a new file specified
+// by "encoded_file_path". Return status indicates whether the operation
+// was successful (either OK_PROFILE_COLLECTION or an error of some sort).
+//
+PROFILE_RESULT encode_to_proto(const std::string &data_file_path,
+ const std::string &encoded_file_path);
diff --git a/perfprofd/perfprofdmain.cc b/perfprofd/perfprofdmain.cc
new file mode 100644
index 00000000..35cdb956
--- /dev/null
+++ b/perfprofd/perfprofdmain.cc
@@ -0,0 +1,23 @@
+/*
+**
+** Copyright 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.
+*/
+
+extern int perfprofd_main(int argc, char** argv);
+
+int main(int argc, char** argv)
+{
+ return perfprofd_main(argc, argv);
+}
diff --git a/perfprofd/perfprofdutils.cc b/perfprofd/perfprofdutils.cc
new file mode 100644
index 00000000..32d55c7a
--- /dev/null
+++ b/perfprofd/perfprofdutils.cc
@@ -0,0 +1,54 @@
+/*
+**
+** Copyright 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.
+*/
+
+#define LOG_TAG "perfprofd"
+
+#include <stdarg.h>
+#include <unistd.h>
+
+#include <utils/Log.h>
+
+#include "perfprofdutils.h"
+
+void perfprofd_log_error(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ LOG_PRI_VA(ANDROID_LOG_ERROR, LOG_TAG, fmt, ap);
+ va_end(ap);
+}
+
+void perfprofd_log_warning(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ LOG_PRI_VA(ANDROID_LOG_WARN, LOG_TAG, fmt, ap);
+ va_end(ap);
+}
+
+void perfprofd_log_info(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ LOG_PRI_VA(ANDROID_LOG_INFO, LOG_TAG, fmt, ap);
+ va_end(ap);
+}
+
+void perfprofd_sleep(int seconds)
+{
+ sleep(seconds);
+}
diff --git a/perfprofd/perfprofdutils.h b/perfprofd/perfprofdutils.h
new file mode 100644
index 00000000..a17356bc
--- /dev/null
+++ b/perfprofd/perfprofdutils.h
@@ -0,0 +1,36 @@
+/*
+**
+** Copyright 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 <sys/cdefs.h>
+
+__BEGIN_DECLS
+
+//
+// These routines are separated out from the core perfprofd so
+// as to be used as part of the unit test (see the README.txt
+// alongside the unit test for more info).
+//
+extern void perfprofd_log_error(const char *fmt, ...);
+extern void perfprofd_log_warning(const char *fmt, ...);
+extern void perfprofd_log_info(const char *fmt, ...);
+extern void perfprofd_sleep(int seconds);
+
+#define W_ALOGE perfprofd_log_error
+#define W_ALOGW perfprofd_log_warning
+#define W_ALOGI perfprofd_log_info
+
+__END_DECLS
diff --git a/perfprofd/quipper/address_mapper.cc b/perfprofd/quipper/address_mapper.cc
new file mode 100644
index 00000000..70a2e5e9
--- /dev/null
+++ b/perfprofd/quipper/address_mapper.cc
@@ -0,0 +1,217 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "address_mapper.h"
+
+#include "base/logging.h"
+
+namespace quipper {
+
+AddressMapper::AddressMapper(const AddressMapper& source) {
+ mappings_ = source.mappings_;
+}
+
+bool AddressMapper::Map(const uint64_t real_addr,
+ const uint64_t size,
+ const bool remove_existing_mappings) {
+ return MapWithID(real_addr, size, kuint64max, 0, remove_existing_mappings);
+}
+
+bool AddressMapper::MapWithID(const uint64_t real_addr,
+ const uint64_t size,
+ const uint64_t id,
+ const uint64_t offset_base,
+ bool remove_existing_mappings) {
+ MappedRange range;
+ range.real_addr = real_addr;
+ range.size = size;
+ range.id = id;
+ range.offset_base = offset_base;
+
+ if (size == 0) {
+ LOG(ERROR) << "Must allocate a nonzero-length address range.";
+ return false;
+ }
+
+ // Check that this mapping does not overflow the address space.
+ if (real_addr + size - 1 != kuint64max &&
+ !(real_addr + size > real_addr)) {
+ DumpToLog();
+ LOG(ERROR) << "Address mapping at " << std::hex << real_addr
+ << " with size " << std::hex << size << " overflows.";
+ return false;
+ }
+
+ // Check for collision with an existing mapping. This must be an overlap that
+ // does not result in one range being completely covered by another
+ MappingList::iterator iter;
+ MappingList mappings_to_delete;
+ bool old_range_found = false;
+ MappedRange old_range;
+ for (iter = mappings_.begin(); iter != mappings_.end(); ++iter) {
+ if (!iter->Intersects(range))
+ continue;
+ // Quit if existing ranges that collide aren't supposed to be removed.
+ if (!remove_existing_mappings)
+ return false;
+ if (!old_range_found && iter->Covers(range) && iter->size > range.size) {
+ old_range_found = true;
+ old_range = *iter;
+ continue;
+ }
+ mappings_to_delete.push_back(*iter);
+ }
+
+ while (!mappings_to_delete.empty()) {
+ const MappedRange& range = mappings_to_delete.front();
+ CHECK(Unmap(range));
+ mappings_to_delete.pop_front();
+ }
+
+ // Otherwise check for this range being covered by another range. If that
+ // happens, split or reduce the existing range to make room.
+ if (old_range_found) {
+ CHECK(Unmap(old_range));
+
+ uint64_t gap_before = range.real_addr - old_range.real_addr;
+ uint64_t gap_after = (old_range.real_addr + old_range.size) -
+ (range.real_addr + range.size);
+
+ if (gap_before) {
+ CHECK(MapWithID(old_range.real_addr,
+ gap_before,
+ old_range.id,
+ old_range.offset_base,
+ false));
+ }
+
+ CHECK(MapWithID(range.real_addr, range.size, id, offset_base, false));
+
+ if (gap_after) {
+ CHECK(MapWithID(range.real_addr + range.size,
+ gap_after,
+ old_range.id,
+ old_range.offset_base + gap_before + range.size,
+ false));
+ }
+
+ return true;
+ }
+
+ // Now search for a location for the new range. It should be in the first
+ // free block in quipper space.
+
+ // If there is no existing mapping, add it to the beginning of quipper space.
+ if (mappings_.empty()) {
+ range.mapped_addr = 0;
+ range.unmapped_space_after = kuint64max - range.size;
+ mappings_.push_back(range);
+ return true;
+ }
+
+ // If there is space before the first mapped range in quipper space, use it.
+ if (mappings_.begin()->mapped_addr >= range.size) {
+ range.mapped_addr = 0;
+ range.unmapped_space_after = mappings_.begin()->mapped_addr - range.size;
+ mappings_.push_front(range);
+ return true;
+ }
+
+ // Otherwise, search through the existing mappings for a free block after one
+ // of them.
+ for (iter = mappings_.begin(); iter != mappings_.end(); ++iter) {
+ if (iter->unmapped_space_after < range.size)
+ continue;
+
+ range.mapped_addr = iter->mapped_addr + iter->size;
+ range.unmapped_space_after = iter->unmapped_space_after - range.size;
+ iter->unmapped_space_after = 0;
+
+ mappings_.insert(++iter, range);
+ return true;
+ }
+
+ // If it still hasn't succeeded in mapping, it means there is no free space in
+ // quipper space large enough for a mapping of this size.
+ DumpToLog();
+ LOG(ERROR) << "Could not find space to map addr=" << std::hex << real_addr
+ << " with size " << std::hex << size;
+ return false;
+}
+
+void AddressMapper::DumpToLog() const {
+ MappingList::const_iterator it;
+ for (it = mappings_.begin(); it != mappings_.end(); ++it) {
+ LOG(INFO) << " real_addr: " << std::hex << it->real_addr
+ << " mapped: " << std::hex << it->mapped_addr
+ << " id: " << std::hex << it->id
+ << " size: " << std::hex << it->size;
+ }
+}
+
+bool AddressMapper::GetMappedAddress(const uint64_t real_addr,
+ uint64_t* mapped_addr) const {
+ CHECK(mapped_addr);
+ MappingList::const_iterator iter;
+ for (iter = mappings_.begin(); iter != mappings_.end(); ++iter) {
+ if (!iter->ContainsAddress(real_addr))
+ continue;
+ *mapped_addr = iter->mapped_addr + real_addr - iter->real_addr;
+ return true;
+ }
+ return false;
+}
+
+bool AddressMapper::GetMappedIDAndOffset(const uint64_t real_addr,
+ uint64_t* id,
+ uint64_t* offset) const {
+ CHECK(id);
+ CHECK(offset);
+ MappingList::const_iterator iter;
+ for (iter = mappings_.begin(); iter != mappings_.end(); ++iter) {
+ if (!iter->ContainsAddress(real_addr))
+ continue;
+ *id = iter->id;
+ *offset = real_addr - iter->real_addr + iter->offset_base;
+ return true;
+ }
+ return false;
+}
+
+uint64_t AddressMapper::GetMaxMappedLength() const {
+ if (IsEmpty())
+ return 0;
+
+ uint64_t min = mappings_.begin()->mapped_addr;
+
+ MappingList::const_iterator iter = mappings_.end();
+ --iter;
+ uint64_t max = iter->mapped_addr + iter->size;
+
+ return max - min;
+}
+
+bool AddressMapper::Unmap(const MappedRange& range) {
+ MappingList::iterator iter;
+ // TODO(sque): this is highly inefficient since Unmap() is called from a
+ // function that has already iterated to the right place within |mappings_|.
+ // For a first revision, I am sacrificing efficiency for of clarity, due to
+ // the trickiness of removing elements using iterators.
+ for (iter = mappings_.begin(); iter != mappings_.end(); ++iter) {
+ if (range.real_addr == iter->real_addr && range.size == iter->size) {
+ // Add the freed up space to the free space counter of the previous
+ // mapped region, if it exists.
+ if (iter != mappings_.begin()) {
+ --iter;
+ iter->unmapped_space_after += range.size + range.unmapped_space_after;
+ ++iter;
+ }
+ mappings_.erase(iter);
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace quipper
diff --git a/perfprofd/quipper/address_mapper.h b/perfprofd/quipper/address_mapper.h
new file mode 100644
index 00000000..ef2d6d21
--- /dev/null
+++ b/perfprofd/quipper/address_mapper.h
@@ -0,0 +1,128 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMIUMOS_WIDE_PROFILING_ADDRESS_MAPPER_H_
+#define CHROMIUMOS_WIDE_PROFILING_ADDRESS_MAPPER_H_
+
+#include <stdint.h>
+
+#include <list>
+
+namespace quipper {
+
+class AddressMapper {
+ public:
+ AddressMapper() {}
+
+ // Copy constructor: copies mappings from |source| to this AddressMapper. This
+ // is useful for copying mappings from parent to child process upon fork(). It
+ // is also useful to copy kernel mappings to any process that is created.
+ AddressMapper(const AddressMapper& source);
+
+ // Maps a new address range to quipper space.
+ // |remove_existing_mappings| indicates whether to remove old mappings that
+ // collide with the new range in real address space, indicating it has been
+ // unmapped.
+ // Returns true if mapping was successful.
+ bool Map(const uint64_t real_addr,
+ const uint64_t length,
+ bool remove_existing_mappings);
+
+ // Like Map(real_addr, length, remove_existing_mappings). |id| is an
+ // identifier value to be stored along with the mapping. AddressMapper does
+ // not care whether it is unique compared to all other IDs passed in. That is
+ // up to the caller to keep track of.
+ // |offset_base| represents the offset within the original region at which the
+ // mapping begins. The original region can be much larger than the mapped
+ // region.
+ // e.g. Given a mapped region with base=0x4000 and size=0x2000 mapped with
+ // offset_base=0x10000, then the address 0x5000 maps to an offset of 0x11000
+ // (0x5000 - 0x4000 + 0x10000).
+ bool MapWithID(const uint64_t real_addr,
+ const uint64_t length,
+ const uint64_t id,
+ const uint64_t offset_base,
+ bool remove_existing_mappings);
+
+ // Looks up |real_addr| and returns the mapped address.
+ bool GetMappedAddress(const uint64_t real_addr, uint64_t* mapped_addr) const;
+
+ // Looks up |real_addr| and returns the mapping's ID and offset from the
+ // start of the mapped space.
+ bool GetMappedIDAndOffset(const uint64_t real_addr,
+ uint64_t* id,
+ uint64_t* offset) const;
+
+ // Returns true if there are no mappings.
+ bool IsEmpty() const {
+ return mappings_.empty();
+ }
+
+ // Returns the number of address ranges that are currently mapped.
+ unsigned int GetNumMappedRanges() const {
+ return mappings_.size();
+ }
+
+ // Returns the maximum length of quipper space containing mapped areas.
+ // There may be gaps in between blocks.
+ // If the result is 2^64 (all of quipper space), this returns 0. Call
+ // IsEmpty() to distinguish this from actual emptiness.
+ uint64_t GetMaxMappedLength() const;
+
+ // Dumps the state of the address mapper to logs. Useful for debugging.
+ void DumpToLog() const;
+
+ private:
+ struct MappedRange {
+ uint64_t real_addr;
+ uint64_t mapped_addr;
+ uint64_t size;
+
+ uint64_t id;
+ uint64_t offset_base;
+
+ // Length of unmapped space after this range.
+ uint64_t unmapped_space_after;
+
+ // Determines if this range intersects another range in real space.
+ inline bool Intersects(const MappedRange& range) const {
+ return (real_addr <= range.real_addr + range.size - 1) &&
+ (real_addr + size - 1 >= range.real_addr);
+ }
+
+ // Determines if this range fully covers another range in real space.
+ inline bool Covers(const MappedRange& range) const {
+ return (real_addr <= range.real_addr) &&
+ (real_addr + size - 1 >= range.real_addr + range.size - 1);
+ }
+
+ // Determines if this range fully contains another range in real space.
+ // This is different from Covers() in that the boundaries cannot overlap.
+ inline bool Contains(const MappedRange& range) const {
+ return (real_addr < range.real_addr) &&
+ (real_addr + size - 1 > range.real_addr + range.size - 1);
+ }
+
+ // Determines if this range contains the given address |addr|.
+ inline bool ContainsAddress(uint64_t addr) const {
+ return (addr >= real_addr && addr <= real_addr + size - 1);
+ }
+ };
+
+ // TODO(sque): implement with set or map to improve searching.
+ typedef std::list<MappedRange> MappingList;
+
+ // Removes an existing address mapping.
+ // Returns true if successful, false if no mapped address range was found.
+ bool Unmap(const MappedRange& range);
+
+ // Container for all the existing mappings.
+ MappingList mappings_;
+
+ bool CheckMappings() const;
+};
+
+} // namespace quipper
+
+#endif // CHROMIUMOS_WIDE_PROFILING_ADDRESS_MAPPER_H_
diff --git a/perfprofd/quipper/base/basictypes.h b/perfprofd/quipper/base/basictypes.h
new file mode 100644
index 00000000..cec5bedd
--- /dev/null
+++ b/perfprofd/quipper/base/basictypes.h
@@ -0,0 +1,58 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains definitions of our old basic integral types
+// ((u)int{8,16,32,64}) and further includes. I recommend that you use the C99
+// standard types instead, and include <stdint.h>/<stddef.h>/etc. as needed.
+// Note that the macros and macro-like constructs that were formerly defined in
+// this file are now available separately in base/macros.h.
+
+#ifndef BASE_BASICTYPES_H_
+#define BASE_BASICTYPES_H_
+
+#include <limits.h> // So we can set the bounds of our types.
+#include <stddef.h> // For size_t.
+#include <stdint.h> // For intptr_t.
+
+#include "quipper/base/macros.h"
+#include "quipper/base/port.h" // Types that only need exist on certain systems.
+
+// DEPRECATED: Please use (u)int{8,16,32,64}_t instead (and include <stdint.h>).
+typedef int8_t int8;
+typedef uint8_t uint8;
+typedef int16_t int16;
+typedef int32_t int32;
+typedef uint16_t uint16;
+typedef uint32_t uint32;
+
+// TODO(vtl): Figure what's up with the 64-bit types. Can we just define them as
+// |int64_t|/|uint64_t|?
+// The NSPR system headers define 64-bit as |long| when possible, except on
+// Mac OS X. In order to not have typedef mismatches, we do the same on LP64.
+//
+// On Mac OS X, |long long| is used for 64-bit types for compatibility with
+// <inttypes.h> format macros even in the LP64 model.
+#if defined(__LP64__) && !defined(OS_MACOSX) && !defined(OS_OPENBSD)
+typedef long int64;
+typedef unsigned long uint64;
+#else
+typedef long long int64;
+typedef unsigned long long uint64;
+#endif
+
+// DEPRECATED: Please use std::numeric_limits (from <limits>) instead.
+const uint8 kuint8max = 0xFF;
+const uint16 kuint16max = 0xFFFF;
+const uint32 kuint32max = 0xFFFFFFFF;
+const uint64 kuint64max = 0xFFFFFFFFFFFFFFFFULL;
+const int8 kint8min = -0x7F - 1;
+const int8 kint8max = 0x7F;
+const int16 kint16min = -0x7FFF - 1;
+const int16 kint16max = 0x7FFF;
+const int32 kint32min = -0x7FFFFFFF - 1;
+const int32 kint32max = 0x7FFFFFFF;
+const int64 kint64min = -0x7FFFFFFFFFFFFFFFLL - 1;
+const int64 kint64max = 0x7FFFFFFFFFFFFFFFLL;
+
+#endif // BASE_BASICTYPES_H_
diff --git a/perfprofd/quipper/base/compiler_specific.h b/perfprofd/quipper/base/compiler_specific.h
new file mode 100644
index 00000000..000c7d74
--- /dev/null
+++ b/perfprofd/quipper/base/compiler_specific.h
@@ -0,0 +1,208 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_COMPILER_SPECIFIC_H_
+#define BASE_COMPILER_SPECIFIC_H_
+
+#include "quipper/build/build_config.h"
+
+#if defined(COMPILER_MSVC)
+
+// Macros for suppressing and disabling warnings on MSVC.
+//
+// Warning numbers are enumerated at:
+// http://msdn.microsoft.com/en-us/library/8x5x43k7(VS.80).aspx
+//
+// The warning pragma:
+// http://msdn.microsoft.com/en-us/library/2c8f766e(VS.80).aspx
+//
+// Using __pragma instead of #pragma inside macros:
+// http://msdn.microsoft.com/en-us/library/d9x1s805.aspx
+
+// MSVC_SUPPRESS_WARNING disables warning |n| for the remainder of the line and
+// for the next line of the source file.
+#define MSVC_SUPPRESS_WARNING(n) __pragma(warning(suppress:n))
+
+// MSVC_PUSH_DISABLE_WARNING pushes |n| onto a stack of warnings to be disabled.
+// The warning remains disabled until popped by MSVC_POP_WARNING.
+#define MSVC_PUSH_DISABLE_WARNING(n) __pragma(warning(push)) \
+ __pragma(warning(disable:n))
+
+// MSVC_PUSH_WARNING_LEVEL pushes |n| as the global warning level. The level
+// remains in effect until popped by MSVC_POP_WARNING(). Use 0 to disable all
+// warnings.
+#define MSVC_PUSH_WARNING_LEVEL(n) __pragma(warning(push, n))
+
+// Pop effects of innermost MSVC_PUSH_* macro.
+#define MSVC_POP_WARNING() __pragma(warning(pop))
+
+#define MSVC_DISABLE_OPTIMIZE() __pragma(optimize("", off))
+#define MSVC_ENABLE_OPTIMIZE() __pragma(optimize("", on))
+
+// Allows exporting a class that inherits from a non-exported base class.
+// This uses suppress instead of push/pop because the delimiter after the
+// declaration (either "," or "{") has to be placed before the pop macro.
+//
+// Example usage:
+// class EXPORT_API Foo : NON_EXPORTED_BASE(public Bar) {
+//
+// MSVC Compiler warning C4275:
+// non dll-interface class 'Bar' used as base for dll-interface class 'Foo'.
+// Note that this is intended to be used only when no access to the base class'
+// static data is done through derived classes or inline methods. For more info,
+// see http://msdn.microsoft.com/en-us/library/3tdb471s(VS.80).aspx
+#define NON_EXPORTED_BASE(code) MSVC_SUPPRESS_WARNING(4275) \
+ code
+
+#else // Not MSVC
+
+#define MSVC_SUPPRESS_WARNING(n)
+#define MSVC_PUSH_DISABLE_WARNING(n)
+#define MSVC_PUSH_WARNING_LEVEL(n)
+#define MSVC_POP_WARNING()
+#define MSVC_DISABLE_OPTIMIZE()
+#define MSVC_ENABLE_OPTIMIZE()
+#define NON_EXPORTED_BASE(code) code
+
+#endif // COMPILER_MSVC
+
+
+// The C++ standard requires that static const members have an out-of-class
+// definition (in a single compilation unit), but MSVC chokes on this (when
+// language extensions, which are required, are enabled). (You're only likely to
+// notice the need for a definition if you take the address of the member or,
+// more commonly, pass it to a function that takes it as a reference argument --
+// probably an STL function.) This macro makes MSVC do the right thing. See
+// http://msdn.microsoft.com/en-us/library/34h23df8(v=vs.100).aspx for more
+// information. Use like:
+//
+// In .h file:
+// struct Foo {
+// static const int kBar = 5;
+// };
+//
+// In .cc file:
+// STATIC_CONST_MEMBER_DEFINITION const int Foo::kBar;
+#if defined(COMPILER_MSVC)
+#define STATIC_CONST_MEMBER_DEFINITION __declspec(selectany)
+#else
+#define STATIC_CONST_MEMBER_DEFINITION
+#endif
+
+// Annotate a variable indicating it's ok if the variable is not used.
+// (Typically used to silence a compiler warning when the assignment
+// is important for some other reason.)
+// Use like:
+// int x ALLOW_UNUSED = ...;
+#if defined(COMPILER_GCC)
+#define ALLOW_UNUSED __attribute__((unused))
+#else
+#define ALLOW_UNUSED
+#endif
+
+// Annotate a function indicating it should not be inlined.
+// Use like:
+// NOINLINE void DoStuff() { ... }
+#if defined(COMPILER_GCC)
+#define NOINLINE __attribute__((noinline))
+#elif defined(COMPILER_MSVC)
+#define NOINLINE __declspec(noinline)
+#else
+#define NOINLINE
+#endif
+
+// Specify memory alignment for structs, classes, etc.
+// Use like:
+// class ALIGNAS(16) MyClass { ... }
+// ALIGNAS(16) int array[4];
+#if defined(COMPILER_MSVC)
+#define ALIGNAS(byte_alignment) __declspec(align(byte_alignment))
+#elif defined(COMPILER_GCC)
+#define ALIGNAS(byte_alignment) __attribute__((aligned(byte_alignment)))
+#endif
+
+// Return the byte alignment of the given type (available at compile time). Use
+// sizeof(type) prior to checking __alignof to workaround Visual C++ bug:
+// http://goo.gl/isH0C
+// Use like:
+// ALIGNOF(int32) // this would be 4
+#if defined(COMPILER_MSVC)
+#define ALIGNOF(type) (sizeof(type) - sizeof(type) + __alignof(type))
+#elif defined(COMPILER_GCC)
+#define ALIGNOF(type) __alignof__(type)
+#endif
+
+// Annotate a virtual method indicating it must be overriding a virtual
+// method in the parent class.
+// Use like:
+// virtual void foo() OVERRIDE;
+#define OVERRIDE override
+
+// Annotate a virtual method indicating that subclasses must not override it,
+// or annotate a class to indicate that it cannot be subclassed.
+// Use like:
+// virtual void foo() FINAL;
+// class B FINAL : public A {};
+#define FINAL final
+
+// Annotate a function indicating the caller must examine the return value.
+// Use like:
+// int foo() WARN_UNUSED_RESULT;
+// To explicitly ignore a result, see |ignore_result()| in <base/basictypes.h>.
+#if defined(COMPILER_GCC)
+#define WARN_UNUSED_RESULT __attribute__((warn_unused_result))
+#else
+#define WARN_UNUSED_RESULT
+#endif
+
+// Tell the compiler a function is using a printf-style format string.
+// |format_param| is the one-based index of the format string parameter;
+// |dots_param| is the one-based index of the "..." parameter.
+// For v*printf functions (which take a va_list), pass 0 for dots_param.
+// (This is undocumented but matches what the system C headers do.)
+#if defined(COMPILER_GCC)
+#define PRINTF_FORMAT(format_param, dots_param) \
+ __attribute__((format(printf, format_param, dots_param)))
+#else
+#define PRINTF_FORMAT(format_param, dots_param)
+#endif
+
+// WPRINTF_FORMAT is the same, but for wide format strings.
+// This doesn't appear to yet be implemented in any compiler.
+// See http://gcc.gnu.org/bugzilla/show_bug.cgi?id=38308 .
+#define WPRINTF_FORMAT(format_param, dots_param)
+// If available, it would look like:
+// __attribute__((format(wprintf, format_param, dots_param)))
+
+// MemorySanitizer annotations.
+#if defined(MEMORY_SANITIZER) && !defined(OS_NACL)
+#include <sanitizer/msan_interface.h>
+
+// Mark a memory region fully initialized.
+// Use this to annotate code that deliberately reads uninitialized data, for
+// example a GC scavenging root set pointers from the stack.
+#define MSAN_UNPOISON(p, s) __msan_unpoison(p, s)
+#else // MEMORY_SANITIZER
+#define MSAN_UNPOISON(p, s)
+#endif // MEMORY_SANITIZER
+
+// Macro useful for writing cross-platform function pointers.
+#if !defined(CDECL)
+#if defined(OS_WIN)
+#define CDECL __cdecl
+#else // defined(OS_WIN)
+#define CDECL
+#endif // defined(OS_WIN)
+#endif // !defined(CDECL)
+
+// Macro for hinting that an expression is likely to be false.
+#if !defined(UNLIKELY)
+#if defined(COMPILER_GCC)
+#define UNLIKELY(x) __builtin_expect(!!(x), 0)
+#else
+#define UNLIKELY(x) (x)
+#endif // defined(COMPILER_GCC)
+#endif // !defined(UNLIKELY)
+
+#endif // BASE_COMPILER_SPECIFIC_H_
diff --git a/perfprofd/quipper/base/logging.cc b/perfprofd/quipper/base/logging.cc
new file mode 100644
index 00000000..cc73d287
--- /dev/null
+++ b/perfprofd/quipper/base/logging.cc
@@ -0,0 +1,110 @@
+//
+// Logging support functions. These are designed to mimic those used in
+// chromium_org/base in terms of interface, but to redirect error to
+// the system log.
+//
+
+#include "quipper/base/logging.h"
+
+#if defined(OS_POSIX)
+#include <errno.h>
+#include <pthread.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#endif
+
+#include <algorithm>
+#include <cstring>
+#include <ctime>
+#include <iomanip>
+#include <ostream>
+#include <string>
+
+#include <android/log.h>
+
+#define LOG_TAG "perf_reader"
+
+namespace logging {
+
+namespace {
+
+int min_log_level = 0;
+
+}
+
+void SetMinLogLevel(int level) {
+ min_log_level = std::min(LOG_FATAL, level);
+}
+
+int GetMinLogLevel() {
+ return min_log_level;
+}
+
+// MSVC doesn't like complex extern templates and DLLs.
+#if !defined(COMPILER_MSVC)
+// Explicit instantiations for commonly used comparisons.
+template std::string* MakeCheckOpString<int, int>(
+ const int&, const int&, const char* names);
+template std::string* MakeCheckOpString<unsigned long, unsigned long>(
+ const unsigned long&, const unsigned long&, const char* names);
+template std::string* MakeCheckOpString<unsigned long, unsigned int>(
+ const unsigned long&, const unsigned int&, const char* names);
+template std::string* MakeCheckOpString<unsigned int, unsigned long>(
+ const unsigned int&, const unsigned long&, const char* names);
+template std::string* MakeCheckOpString<std::string, std::string>(
+ const std::string&, const std::string&, const char* name);
+#endif
+
+LogMessage::LogMessage(const char* file, int line, LogSeverity severity)
+ : severity_(severity), file_(file), line_(line) {
+ Init(file, line);
+}
+
+LogMessage::LogMessage(const char* file, int line, std::string* result)
+ : severity_(LOG_FATAL), file_(file), line_(line) {
+ Init(file, line);
+ stream_ << "Check failed: " << *result;
+ delete result;
+}
+
+LogMessage::LogMessage(const char* file, int line, LogSeverity severity,
+ std::string* result)
+ : severity_(severity), file_(file), line_(line) {
+ Init(file, line);
+ stream_ << "Check failed: " << *result;
+ delete result;
+}
+
+LogMessage::~LogMessage() {
+ stream_ << std::endl;
+ std::string str_newline(stream_.str());
+
+ android_LogPriority priority =
+ (severity_ < 0) ? ANDROID_LOG_VERBOSE : ANDROID_LOG_UNKNOWN;
+ switch (severity_) {
+ case LOG_INFO:
+ priority = ANDROID_LOG_INFO;
+ break;
+ case LOG_WARNING:
+ priority = ANDROID_LOG_WARN;
+ break;
+ case LOG_ERROR:
+ priority = ANDROID_LOG_ERROR;
+ break;
+ case LOG_FATAL:
+ priority = ANDROID_LOG_FATAL;
+ break;
+ }
+ __android_log_write(priority, LOG_TAG, str_newline.c_str());
+
+ if (severity_ == LOG_FATAL) {
+ exit(9);
+ }
+}
+
+void LogMessage::Init(const char* /* file */, int /* line */) {
+}
+
+} // namespace logging
diff --git a/perfprofd/quipper/base/logging.h b/perfprofd/quipper/base/logging.h
new file mode 100644
index 00000000..2851d914
--- /dev/null
+++ b/perfprofd/quipper/base/logging.h
@@ -0,0 +1,671 @@
+
+#ifndef BASE_LOGGING_H_
+#define BASE_LOGGING_H_
+
+#include <cassert>
+#include <string>
+#include <cstring>
+#include <sstream>
+
+#include "quipper/base/macros.h"
+#include "quipper/base/basictypes.h"
+
+//
+// Logging macros designed to mimic those used in chromium_org/base.
+//
+
+// Instructions
+// ------------
+//
+// Make a bunch of macros for logging. The way to log things is to stream
+// things to LOG(<a particular severity level>). E.g.,
+//
+// LOG(INFO) << "Found " << num_cookies << " cookies";
+//
+// You can also do conditional logging:
+//
+// LOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
+//
+// The CHECK(condition) macro is active in both debug and release builds and
+// effectively performs a LOG(FATAL) which terminates the process and
+// generates a crashdump unless a debugger is attached.
+//
+// There are also "debug mode" logging macros like the ones above:
+//
+// DLOG(INFO) << "Found cookies";
+//
+// DLOG_IF(INFO, num_cookies > 10) << "Got lots of cookies";
+//
+// All "debug mode" logging is compiled away to nothing for non-debug mode
+// compiles. LOG_IF and development flags also work well together
+// because the code can be compiled away sometimes.
+//
+// We also have
+//
+// LOG_ASSERT(assertion);
+// DLOG_ASSERT(assertion);
+//
+// which is syntactic sugar for {,D}LOG_IF(FATAL, assert fails) << assertion;
+//
+// There are "verbose level" logging macros. They look like
+//
+// VLOG(1) << "I'm printed when you run the program with --v=1 or more";
+// VLOG(2) << "I'm printed when you run the program with --v=2 or more";
+//
+// These always log at the INFO log level (when they log at all).
+// The verbose logging can also be turned on module-by-module. For instance,
+// --vmodule=profile=2,icon_loader=1,browser_*=3,*/chromeos/*=4 --v=0
+// will cause:
+// a. VLOG(2) and lower messages to be printed from profile.{h,cc}
+// b. VLOG(1) and lower messages to be printed from icon_loader.{h,cc}
+// c. VLOG(3) and lower messages to be printed from files prefixed with
+// "browser"
+// d. VLOG(4) and lower messages to be printed from files under a
+// "chromeos" directory.
+// e. VLOG(0) and lower messages to be printed from elsewhere
+//
+// The wildcarding functionality shown by (c) supports both '*' (match
+// 0 or more characters) and '?' (match any single character)
+// wildcards. Any pattern containing a forward or backward slash will
+// be tested against the whole pathname and not just the module.
+// E.g., "*/foo/bar/*=2" would change the logging level for all code
+// in source files under a "foo/bar" directory.
+//
+// There's also VLOG_IS_ON(n) "verbose level" condition macro. To be used as
+//
+// if (VLOG_IS_ON(2)) {
+// // do some logging preparation and logging
+// // that can't be accomplished with just VLOG(2) << ...;
+// }
+//
+// There is also a VLOG_IF "verbose level" condition macro for sample
+// cases, when some extra computation and preparation for logs is not
+// needed.
+//
+// VLOG_IF(1, (size > 1024))
+// << "I'm printed when size is more than 1024 and when you run the "
+// "program with --v=1 or more";
+//
+// We also override the standard 'assert' to use 'DLOG_ASSERT'.
+//
+// Lastly, there is:
+//
+// PLOG(ERROR) << "Couldn't do foo";
+// DPLOG(ERROR) << "Couldn't do foo";
+// PLOG_IF(ERROR, cond) << "Couldn't do foo";
+// DPLOG_IF(ERROR, cond) << "Couldn't do foo";
+// PCHECK(condition) << "Couldn't do foo";
+// DPCHECK(condition) << "Couldn't do foo";
+//
+// which append the last system error to the message in string form (taken from
+// GetLastError() on Windows and errno on POSIX).
+//
+// The supported severity levels for macros that allow you to specify one
+// are (in increasing order of severity) INFO, WARNING, ERROR, and FATAL.
+//
+// Very important: logging a message at the FATAL severity level causes
+// the program to terminate (after the message is logged).
+//
+// There is the special severity of DFATAL, which logs FATAL in debug mode,
+// ERROR in normal mode.
+
+#define BASE_EXPORT
+
+namespace logging {
+
+// Sets the log level. Anything at or above this level will be written to the
+// log file/displayed to the user (if applicable). Anything below this level
+// will be silently ignored. The log level defaults to 0 (everything is logged
+// up to level INFO) if this function is not called.
+// Note that log messages for VLOG(x) are logged at level -x, so setting
+// the min log level to negative values enables verbose logging.
+BASE_EXPORT void SetMinLogLevel(int level);
+
+// Gets the current log level.
+BASE_EXPORT int GetMinLogLevel();
+
+// Gets the VLOG default verbosity level.
+BASE_EXPORT int GetVlogVerbosity();
+
+typedef int LogSeverity;
+const LogSeverity LOG_VERBOSE = -1; // This is level 1 verbosity
+// Note: the log severities are used to index into the array of names,
+// see log_severity_names.
+const LogSeverity LOG_INFO = 0;
+const LogSeverity LOG_WARNING = 1;
+const LogSeverity LOG_ERROR = 2;
+const LogSeverity LOG_FATAL = 3;
+const LogSeverity LOG_NUM_SEVERITIES = 4;
+
+// A few definitions of macros that don't generate much code. These are used
+// by LOG() and LOG_IF, etc. Since these are used all over our code, it's
+// better to have compact code for these operations.
+#define COMPACT_GOOGLE_LOG_EX_INFO(ClassName, ...) \
+ logging::ClassName(__FILE__, __LINE__, logging::LOG_INFO , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_WARNING(ClassName, ...) \
+ logging::ClassName(__FILE__, __LINE__, logging::LOG_WARNING , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_ERROR(ClassName, ...) \
+ logging::ClassName(__FILE__, __LINE__, logging::LOG_ERROR , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_FATAL(ClassName, ...) \
+ logging::ClassName(__FILE__, __LINE__, logging::LOG_FATAL , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_EX_DFATAL(ClassName, ...) \
+ logging::ClassName(__FILE__, __LINE__, logging::LOG_DFATAL , ##__VA_ARGS__)
+
+#define COMPACT_GOOGLE_LOG_INFO \
+ COMPACT_GOOGLE_LOG_EX_INFO(LogMessage)
+#define COMPACT_GOOGLE_LOG_WARNING \
+ COMPACT_GOOGLE_LOG_EX_WARNING(LogMessage)
+#define COMPACT_GOOGLE_LOG_ERROR \
+ COMPACT_GOOGLE_LOG_EX_ERROR(LogMessage)
+#define COMPACT_GOOGLE_LOG_FATAL \
+ COMPACT_GOOGLE_LOG_EX_FATAL(LogMessage)
+#define COMPACT_GOOGLE_LOG_DFATAL \
+ COMPACT_GOOGLE_LOG_EX_DFATAL(LogMessage)
+
+// As special cases, we can assume that LOG_IS_ON(FATAL) always holds. Also,
+// LOG_IS_ON(DFATAL) always holds in debug mode. In particular, CHECK()s will
+// always fire if they fail.
+#define LOG_IS_ON(severity) \
+ ((::logging::LOG_ ## severity) >= ::logging::GetMinLogLevel())
+
+#define VLOG_IS_ON(verboselevel) false
+
+// Helper macro which avoids evaluating the arguments to a stream if
+// the condition doesn't hold.
+#define LAZY_STREAM(stream, condition) \
+ !(condition) ? (void) 0 : ::logging::LogMessageVoidify() & (stream)
+
+// We use the preprocessor's merging operator, "##", so that, e.g.,
+// LOG(INFO) becomes the token COMPACT_GOOGLE_LOG_INFO. There's some funny
+// subtle difference between ostream member streaming functions (e.g.,
+// ostream::operator<<(int) and ostream non-member streaming functions
+// (e.g., ::operator<<(ostream&, string&): it turns out that it's
+// impossible to stream something like a string directly to an unnamed
+// ostream. We employ a neat hack by calling the stream() member
+// function of LogMessage which seems to avoid the problem.
+#define LOG_STREAM(severity) COMPACT_GOOGLE_LOG_ ## severity.stream()
+
+#define LOG(severity) LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity))
+#define LOG_IF(severity, condition) \
+ LAZY_STREAM(LOG_STREAM(severity), LOG_IS_ON(severity) && (condition))
+
+// The VLOG macros log with negative verbosities.
+#define VLOG_STREAM(verbose_level) \
+ logging::LogMessage(__FILE__, __LINE__, -verbose_level).stream()
+
+#define VLOG(verbose_level) \
+ LAZY_STREAM(VLOG_STREAM(verbose_level), VLOG_IS_ON(verbose_level))
+
+#define VLOG_IF(verbose_level, condition) \
+ LAZY_STREAM(VLOG_STREAM(verbose_level), \
+ VLOG_IS_ON(verbose_level) && (condition))
+
+// TODO(akalin): Add more VLOG variants, e.g. VPLOG.
+
+#define LOG_ASSERT(condition) \
+ LOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". "
+#define SYSLOG_ASSERT(condition) \
+ SYSLOG_IF(FATAL, !(condition)) << "Assert failed: " #condition ". "
+
+#define PLOG(severity) \
+ LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity))
+
+#define PLOG_IF(severity, condition) \
+ LAZY_STREAM(PLOG_STREAM(severity), LOG_IS_ON(severity) && (condition))
+
+// The actual stream used isn't important.
+#define EAT_STREAM_PARAMETERS \
+ true ? (void) 0 : ::logging::LogMessageVoidify() & LOG_STREAM(FATAL)
+
+// CHECK dies with a fatal error if condition is not true. It is *not*
+// controlled by NDEBUG, so the check will be executed regardless of
+// compilation mode.
+//
+// We make sure CHECK et al. always evaluates their arguments, as
+// doing CHECK(FunctionWithSideEffect()) is a common idiom.
+
+#define CHECK(condition) \
+ LAZY_STREAM(LOG_STREAM(FATAL), !(condition)) \
+ << "Check failed: " #condition ". "
+
+#define PCHECK(condition) \
+ LAZY_STREAM(PLOG_STREAM(FATAL), !(condition)) \
+ << "Check failed: " #condition ". "
+
+// Helper macro for binary operators.
+// Don't use this macro directly in your code, use CHECK_EQ et al below.
+//
+// TODO(akalin): Rewrite this so that constructs like if (...)
+// CHECK_EQ(...) else { ... } work properly.
+#define CHECK_OP(name, op, val1, val2) \
+ if (std::string* _result = \
+ logging::Check##name##Impl((val1), (val2), \
+ #val1 " " #op " " #val2)) \
+ logging::LogMessage(__FILE__, __LINE__, _result).stream()
+
+// Build the error message string. This is separate from the "Impl"
+// function template because it is not performance critical and so can
+// be out of line, while the "Impl" code should be inline. Caller
+// takes ownership of the returned string.
+template<class t1, class t2>
+std::string* MakeCheckOpString(const t1& v1, const t2& v2, const char* names) {
+ std::ostringstream ss;
+ ss << names << " (" << v1 << " vs. " << v2 << ")";
+ std::string* msg = new std::string(ss.str());
+ return msg;
+}
+
+// MSVC doesn't like complex extern templates and DLLs.
+#if !defined(COMPILER_MSVC)
+// Commonly used instantiations of MakeCheckOpString<>. Explicitly instantiated
+// in logging.cc.
+extern template BASE_EXPORT std::string* MakeCheckOpString<int, int>(
+ const int&, const int&, const char* names);
+extern template BASE_EXPORT
+std::string* MakeCheckOpString<unsigned long, unsigned long>(
+ const unsigned long&, const unsigned long&, const char* names);
+extern template BASE_EXPORT
+std::string* MakeCheckOpString<unsigned long, unsigned int>(
+ const unsigned long&, const unsigned int&, const char* names);
+extern template BASE_EXPORT
+std::string* MakeCheckOpString<unsigned int, unsigned long>(
+ const unsigned int&, const unsigned long&, const char* names);
+extern template BASE_EXPORT
+std::string* MakeCheckOpString<std::string, std::string>(
+ const std::string&, const std::string&, const char* name);
+#endif
+
+// Helper functions for CHECK_OP macro.
+// The (int, int) specialization works around the issue that the compiler
+// will not instantiate the template version of the function on values of
+// unnamed enum type - see comment below.
+#define DEFINE_CHECK_OP_IMPL(name, op) \
+ template <class t1, class t2> \
+ inline std::string* Check##name##Impl(const t1& v1, const t2& v2, \
+ const char* names) { \
+ if (v1 op v2) return NULL; \
+ else return MakeCheckOpString(v1, v2, names); \
+ } \
+ inline std::string* Check##name##Impl(int v1, int v2, const char* names) { \
+ if (v1 op v2) return NULL; \
+ else return MakeCheckOpString(v1, v2, names); \
+ }
+DEFINE_CHECK_OP_IMPL(EQ, ==)
+DEFINE_CHECK_OP_IMPL(NE, !=)
+DEFINE_CHECK_OP_IMPL(LE, <=)
+DEFINE_CHECK_OP_IMPL(LT, < )
+DEFINE_CHECK_OP_IMPL(GE, >=)
+DEFINE_CHECK_OP_IMPL(GT, > )
+#undef DEFINE_CHECK_OP_IMPL
+
+#define CHECK_EQ(val1, val2) CHECK_OP(EQ, ==, val1, val2)
+#define CHECK_NE(val1, val2) CHECK_OP(NE, !=, val1, val2)
+#define CHECK_LE(val1, val2) CHECK_OP(LE, <=, val1, val2)
+#define CHECK_LT(val1, val2) CHECK_OP(LT, < , val1, val2)
+#define CHECK_GE(val1, val2) CHECK_OP(GE, >=, val1, val2)
+#define CHECK_GT(val1, val2) CHECK_OP(GT, > , val1, val2)
+
+#if defined(NDEBUG)
+#define ENABLE_DLOG 0
+#else
+#define ENABLE_DLOG 1
+#endif
+
+#if defined(NDEBUG) && !defined(DCHECK_ALWAYS_ON)
+#define DCHECK_IS_ON 0
+#else
+#define DCHECK_IS_ON 1
+#endif
+
+// Definitions for DLOG et al.
+
+#if ENABLE_DLOG
+
+#define DLOG_IS_ON(severity) LOG_IS_ON(severity)
+#define DLOG_IF(severity, condition) LOG_IF(severity, condition)
+#define DLOG_ASSERT(condition) LOG_ASSERT(condition)
+#define DPLOG_IF(severity, condition) PLOG_IF(severity, condition)
+#define DVLOG_IF(verboselevel, condition) VLOG_IF(verboselevel, condition)
+#define DVPLOG_IF(verboselevel, condition) VPLOG_IF(verboselevel, condition)
+
+#else // ENABLE_DLOG
+
+// If ENABLE_DLOG is off, we want to avoid emitting any references to
+// |condition| (which may reference a variable defined only if NDEBUG
+// is not defined). Contrast this with DCHECK et al., which has
+// different behavior.
+
+#define DLOG_IS_ON(severity) false
+#define DLOG_IF(severity, condition) EAT_STREAM_PARAMETERS
+#define DLOG_ASSERT(condition) EAT_STREAM_PARAMETERS
+#define DPLOG_IF(severity, condition) EAT_STREAM_PARAMETERS
+#define DVLOG_IF(verboselevel, condition) EAT_STREAM_PARAMETERS
+#define DVPLOG_IF(verboselevel, condition) EAT_STREAM_PARAMETERS
+
+#endif // ENABLE_DLOG
+
+// DEBUG_MODE is for uses like
+// if (DEBUG_MODE) foo.CheckThatFoo();
+// instead of
+// #ifndef NDEBUG
+// foo.CheckThatFoo();
+// #endif
+//
+// We tie its state to ENABLE_DLOG.
+enum { DEBUG_MODE = ENABLE_DLOG };
+
+#undef ENABLE_DLOG
+
+#define DLOG(severity) \
+ LAZY_STREAM(LOG_STREAM(severity), DLOG_IS_ON(severity))
+
+#define DPLOG(severity) \
+ LAZY_STREAM(PLOG_STREAM(severity), DLOG_IS_ON(severity))
+
+#define DVLOG(verboselevel) DVLOG_IF(verboselevel, VLOG_IS_ON(verboselevel))
+
+#define DVPLOG(verboselevel) DVPLOG_IF(verboselevel, VLOG_IS_ON(verboselevel))
+
+// Definitions for DCHECK et al.
+
+#if DCHECK_IS_ON
+
+#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \
+ COMPACT_GOOGLE_LOG_EX_FATAL(ClassName , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_FATAL
+const LogSeverity LOG_DCHECK = LOG_FATAL;
+
+#else // DCHECK_IS_ON
+
+// These are just dummy values.
+#define COMPACT_GOOGLE_LOG_EX_DCHECK(ClassName, ...) \
+ COMPACT_GOOGLE_LOG_EX_INFO(ClassName , ##__VA_ARGS__)
+#define COMPACT_GOOGLE_LOG_DCHECK COMPACT_GOOGLE_LOG_INFO
+const LogSeverity LOG_DCHECK = LOG_INFO;
+
+#endif // DCHECK_IS_ON
+
+// DCHECK et al. make sure to reference |condition| regardless of
+// whether DCHECKs are enabled; this is so that we don't get unused
+// variable warnings if the only use of a variable is in a DCHECK.
+// This behavior is different from DLOG_IF et al.
+
+#define DCHECK(condition) \
+ LAZY_STREAM(LOG_STREAM(DCHECK), DCHECK_IS_ON && !(condition)) \
+ << "Check failed: " #condition ". "
+
+#define DPCHECK(condition) \
+ LAZY_STREAM(PLOG_STREAM(DCHECK), DCHECK_IS_ON && !(condition)) \
+ << "Check failed: " #condition ". "
+
+// Helper macro for binary operators.
+// Don't use this macro directly in your code, use DCHECK_EQ et al below.
+#define DCHECK_OP(name, op, val1, val2) \
+ if (DCHECK_IS_ON) \
+ if (std::string* _result = \
+ logging::Check##name##Impl((val1), (val2), \
+ #val1 " " #op " " #val2)) \
+ logging::LogMessage( \
+ __FILE__, __LINE__, ::logging::LOG_DCHECK, \
+ _result).stream()
+
+// Equality/Inequality checks - compare two values, and log a
+// LOG_DCHECK message including the two values when the result is not
+// as expected. The values must have operator<<(ostream, ...)
+// defined.
+//
+// You may append to the error message like so:
+// DCHECK_NE(1, 2) << ": The world must be ending!";
+//
+// We are very careful to ensure that each argument is evaluated exactly
+// once, and that anything which is legal to pass as a function argument is
+// legal here. In particular, the arguments may be temporary expressions
+// which will end up being destroyed at the end of the apparent statement,
+// for example:
+// DCHECK_EQ(string("abc")[1], 'b');
+//
+// WARNING: These may not compile correctly if one of the arguments is a pointer
+// and the other is NULL. To work around this, simply static_cast NULL to the
+// type of the desired pointer.
+
+#define DCHECK_EQ(val1, val2) DCHECK_OP(EQ, ==, val1, val2)
+#define DCHECK_NE(val1, val2) DCHECK_OP(NE, !=, val1, val2)
+#define DCHECK_LE(val1, val2) DCHECK_OP(LE, <=, val1, val2)
+#define DCHECK_LT(val1, val2) DCHECK_OP(LT, < , val1, val2)
+#define DCHECK_GE(val1, val2) DCHECK_OP(GE, >=, val1, val2)
+#define DCHECK_GT(val1, val2) DCHECK_OP(GT, > , val1, val2)
+
+#if defined(NDEBUG) && defined(OS_CHROMEOS)
+#define NOTREACHED() LOG(ERROR) << "NOTREACHED() hit in " << \
+ __FUNCTION__ << ". "
+#else
+#define NOTREACHED() DCHECK(false)
+#endif
+
+// Redefine the standard assert to use our nice log files
+#undef assert
+#define assert(x) DLOG_ASSERT(x)
+
+// This class more or less represents a particular log message. You
+// create an instance of LogMessage and then stream stuff to it.
+// When you finish streaming to it, ~LogMessage is called and the
+// full message gets streamed to the appropriate destination.
+//
+// You shouldn't actually use LogMessage's constructor to log things,
+// though. You should use the LOG() macro (and variants thereof)
+// above.
+class BASE_EXPORT LogMessage {
+ public:
+ // Used for LOG(severity).
+ LogMessage(const char* file, int line, LogSeverity severity);
+
+ // Used for CHECK_EQ(), etc. Takes ownership of the given string.
+ // Implied severity = LOG_FATAL.
+ LogMessage(const char* file, int line, std::string* result);
+
+ // Used for DCHECK_EQ(), etc. Takes ownership of the given string.
+ LogMessage(const char* file, int line, LogSeverity severity,
+ std::string* result);
+
+ ~LogMessage();
+
+ std::ostream& stream() { return stream_; }
+
+ private:
+ void Init(const char* file, int line);
+
+ LogSeverity severity_;
+ std::ostringstream stream_;
+ size_t message_start_; // Offset of the start of the message (past prefix
+ // info).
+ // The file and line information passed in to the constructor.
+ const char* file_;
+ const int line_;
+
+#if defined(OS_WIN)
+ // Stores the current value of GetLastError in the constructor and restores
+ // it in the destructor by calling SetLastError.
+ // This is useful since the LogMessage class uses a lot of Win32 calls
+ // that will lose the value of GLE and the code that called the log function
+ // will have lost the thread error value when the log call returns.
+ class SaveLastError {
+ public:
+ SaveLastError();
+ ~SaveLastError();
+
+ unsigned long get_error() const { return last_error_; }
+
+ protected:
+ unsigned long last_error_;
+ };
+
+ SaveLastError last_error_;
+#endif
+
+ DISALLOW_COPY_AND_ASSIGN(LogMessage);
+};
+
+// A non-macro interface to the log facility; (useful
+// when the logging level is not a compile-time constant).
+inline void LogAtLevel(int const log_level, std::string const &msg) {
+ LogMessage(__FILE__, __LINE__, log_level).stream() << msg;
+}
+
+// This class is used to explicitly ignore values in the conditional
+// logging macros. This avoids compiler warnings like "value computed
+// is not used" and "statement has no effect".
+class LogMessageVoidify {
+ public:
+ LogMessageVoidify() { }
+ // This has to be an operator with a precedence lower than << but
+ // higher than ?:
+ void operator&(std::ostream&) { }
+};
+
+#if defined(OS_WIN)
+typedef unsigned long SystemErrorCode;
+#elif defined(OS_POSIX)
+typedef int SystemErrorCode;
+#endif
+
+// Alias for ::GetLastError() on Windows and errno on POSIX. Avoids having to
+// pull in windows.h just for GetLastError() and DWORD.
+BASE_EXPORT SystemErrorCode GetLastSystemErrorCode();
+BASE_EXPORT std::string SystemErrorCodeToString(SystemErrorCode error_code);
+
+#if defined(OS_WIN)
+// Appends a formatted system message of the GetLastError() type.
+class BASE_EXPORT Win32ErrorLogMessage {
+ public:
+ Win32ErrorLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err);
+
+ // Appends the error message before destructing the encapsulated class.
+ ~Win32ErrorLogMessage();
+
+ std::ostream& stream() { return log_message_.stream(); }
+
+ private:
+ SystemErrorCode err_;
+ LogMessage log_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(Win32ErrorLogMessage);
+};
+#elif defined(OS_POSIX)
+// Appends a formatted system message of the errno type
+class BASE_EXPORT ErrnoLogMessage {
+ public:
+ ErrnoLogMessage(const char* file,
+ int line,
+ LogSeverity severity,
+ SystemErrorCode err);
+
+ // Appends the error message before destructing the encapsulated class.
+ ~ErrnoLogMessage();
+
+ std::ostream& stream() { return log_message_.stream(); }
+
+ private:
+ SystemErrorCode err_;
+ LogMessage log_message_;
+
+ DISALLOW_COPY_AND_ASSIGN(ErrnoLogMessage);
+};
+#endif // OS_WIN
+
+// Closes the log file explicitly if open.
+// NOTE: Since the log file is opened as necessary by the action of logging
+// statements, there's no guarantee that it will stay closed
+// after this call.
+BASE_EXPORT void CloseLogFile();
+
+// Async signal safe logging mechanism.
+BASE_EXPORT void RawLog(int level, const char* message);
+
+#define RAW_LOG(level, message) logging::RawLog(logging::LOG_ ## level, message)
+
+#define RAW_CHECK(condition) \
+ do { \
+ if (!(condition)) \
+ logging::RawLog(logging::LOG_FATAL, "Check failed: " #condition "\n"); \
+ } while (0)
+
+#if defined(OS_WIN)
+// Returns the default log file path.
+BASE_EXPORT std::wstring GetLogFileFullPath();
+#endif
+
+} // namespace logging
+
+// Note that "The behavior of a C++ program is undefined if it adds declarations
+// or definitions to namespace std or to a namespace within namespace std unless
+// otherwise specified." --C++11[namespace.std]
+//
+// We've checked that this particular definition has the intended behavior on
+// our implementations, but it's prone to breaking in the future, and please
+// don't imitate this in your own definitions without checking with some
+// standard library experts.
+namespace std {
+// These functions are provided as a convenience for logging, which is where we
+// use streams (it is against Google style to use streams in other places). It
+// is designed to allow you to emit non-ASCII Unicode strings to the log file,
+// which is normally ASCII. It is relatively slow, so try not to use it for
+// common cases. Non-ASCII characters will be converted to UTF-8 by these
+// operators.
+BASE_EXPORT std::ostream& operator<<(std::ostream& out, const wchar_t* wstr);
+inline std::ostream& operator<<(std::ostream& out, const std::wstring& wstr) {
+ return out << wstr.c_str();
+}
+} // namespace std
+
+// The NOTIMPLEMENTED() macro annotates codepaths which have
+// not been implemented yet.
+//
+// The implementation of this macro is controlled by NOTIMPLEMENTED_POLICY:
+// 0 -- Do nothing (stripped by compiler)
+// 1 -- Warn at compile time
+// 2 -- Fail at compile time
+// 3 -- Fail at runtime (DCHECK)
+// 4 -- [default] LOG(ERROR) at runtime
+// 5 -- LOG(ERROR) at runtime, only once per call-site
+
+#ifndef NOTIMPLEMENTED_POLICY
+#if defined(OS_ANDROID) && defined(OFFICIAL_BUILD)
+#define NOTIMPLEMENTED_POLICY 0
+#else
+// WebView: Hide NOTIMPLEMENTED entirely in Android release branch.
+#define NOTIMPLEMENTED_POLICY 0
+#endif
+#endif
+
+#if defined(COMPILER_GCC)
+// On Linux, with GCC, we can use __PRETTY_FUNCTION__ to get the demangled name
+// of the current function in the NOTIMPLEMENTED message.
+#define NOTIMPLEMENTED_MSG "Not implemented reached in " << __PRETTY_FUNCTION__
+#else
+#define NOTIMPLEMENTED_MSG "NOT IMPLEMENTED"
+#endif
+
+#if NOTIMPLEMENTED_POLICY == 0
+#define NOTIMPLEMENTED() EAT_STREAM_PARAMETERS
+#elif NOTIMPLEMENTED_POLICY == 1
+// TODO, figure out how to generate a warning
+#define NOTIMPLEMENTED() COMPILE_ASSERT(false, NOT_IMPLEMENTED)
+#elif NOTIMPLEMENTED_POLICY == 2
+#define NOTIMPLEMENTED() COMPILE_ASSERT(false, NOT_IMPLEMENTED)
+#elif NOTIMPLEMENTED_POLICY == 3
+#define NOTIMPLEMENTED() NOTREACHED()
+#elif NOTIMPLEMENTED_POLICY == 4
+#define NOTIMPLEMENTED() LOG(ERROR) << NOTIMPLEMENTED_MSG
+#elif NOTIMPLEMENTED_POLICY == 5
+#define NOTIMPLEMENTED() do {\
+ static bool logged_once = false;\
+ LOG_IF(ERROR, !logged_once) << NOTIMPLEMENTED_MSG;\
+ logged_once = true;\
+} while(0);\
+EAT_STREAM_PARAMETERS
+#endif
+
+#endif // BASE_LOGGING_H_
diff --git a/perfprofd/quipper/base/macros.h b/perfprofd/quipper/base/macros.h
new file mode 100644
index 00000000..57eaa810
--- /dev/null
+++ b/perfprofd/quipper/base/macros.h
@@ -0,0 +1,257 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file contains macros and macro-like constructs (e.g., templates) that
+// are commonly used throughout Chromium source. (It may also contain things
+// that are closely related to things that are commonly used that belong in this
+// file.)
+
+#ifndef BASE_MACROS_H_
+#define BASE_MACROS_H_
+
+#include <stddef.h> // For size_t.
+#include <string.h> // For memcpy.
+
+#include "quipper/base/compiler_specific.h" // For ALLOW_UNUSED.
+
+// Put this in the private: declarations for a class to be uncopyable.
+#define DISALLOW_COPY(TypeName) \
+ TypeName(const TypeName&)
+
+// Put this in the private: declarations for a class to be unassignable.
+#define DISALLOW_ASSIGN(TypeName) \
+ void operator=(const TypeName&)
+
+// A macro to disallow the copy constructor and operator= functions
+// This should be used in the private: declarations for a class
+#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
+ TypeName(const TypeName&); \
+ void operator=(const TypeName&)
+
+// An older, deprecated, politically incorrect name for the above.
+// NOTE: The usage of this macro was banned from our code base, but some
+// third_party libraries are yet using it.
+// TODO(tfarina): Figure out how to fix the usage of this macro in the
+// third_party libraries and get rid of it.
+#define DISALLOW_EVIL_CONSTRUCTORS(TypeName) DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+// A macro to disallow all the implicit constructors, namely the
+// default constructor, copy constructor and operator= functions.
+//
+// This should be used in the private: declarations for a class
+// that wants to prevent anyone from instantiating it. This is
+// especially useful for classes containing only static methods.
+#define DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
+ TypeName(); \
+ DISALLOW_COPY_AND_ASSIGN(TypeName)
+
+// The arraysize(arr) macro returns the # of elements in an array arr.
+// The expression is a compile-time constant, and therefore can be
+// used in defining new arrays, for example. If you use arraysize on
+// a pointer by mistake, you will get a compile-time error.
+//
+// One caveat is that arraysize() doesn't accept any array of an
+// anonymous type or a type defined inside a function. In these rare
+// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is
+// due to a limitation in C++'s template system. The limitation might
+// eventually be removed, but it hasn't happened yet.
+
+// This template function declaration is used in defining arraysize.
+// Note that the function doesn't need an implementation, as we only
+// use its type.
+template <typename T, size_t N>
+char (&ArraySizeHelper(T (&array)[N]))[N];
+
+// That gcc wants both of these prototypes seems mysterious. VC, for
+// its part, can't decide which to use (another mystery). Matching of
+// template overloads: the final frontier.
+#ifndef _MSC_VER
+template <typename T, size_t N>
+char (&ArraySizeHelper(const T (&array)[N]))[N];
+#endif
+
+#define arraysize(array) (sizeof(ArraySizeHelper(array)))
+
+// ARRAYSIZE_UNSAFE performs essentially the same calculation as arraysize,
+// but can be used on anonymous types or types defined inside
+// functions. It's less safe than arraysize as it accepts some
+// (although not all) pointers. Therefore, you should use arraysize
+// whenever possible.
+//
+// The expression ARRAYSIZE_UNSAFE(a) is a compile-time constant of type
+// size_t.
+//
+// ARRAYSIZE_UNSAFE catches a few type errors. If you see a compiler error
+//
+// "warning: division by zero in ..."
+//
+// when using ARRAYSIZE_UNSAFE, you are (wrongfully) giving it a pointer.
+// You should only use ARRAYSIZE_UNSAFE on statically allocated arrays.
+//
+// The following comments are on the implementation details, and can
+// be ignored by the users.
+//
+// ARRAYSIZE_UNSAFE(arr) works by inspecting sizeof(arr) (the # of bytes in
+// the array) and sizeof(*(arr)) (the # of bytes in one array
+// element). If the former is divisible by the latter, perhaps arr is
+// indeed an array, in which case the division result is the # of
+// elements in the array. Otherwise, arr cannot possibly be an array,
+// and we generate a compiler error to prevent the code from
+// compiling.
+//
+// Since the size of bool is implementation-defined, we need to cast
+// !(sizeof(a) & sizeof(*(a))) to size_t in order to ensure the final
+// result has type size_t.
+//
+// This macro is not perfect as it wrongfully accepts certain
+// pointers, namely where the pointer size is divisible by the pointee
+// size. Since all our code has to go through a 32-bit compiler,
+// where a pointer is 4 bytes, this means all pointers to a type whose
+// size is 3 or greater than 4 will be (righteously) rejected.
+
+#define ARRAYSIZE_UNSAFE(a) \
+ ((sizeof(a) / sizeof(*(a))) / \
+ static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
+
+
+// Use implicit_cast as a safe version of static_cast or const_cast
+// for upcasting in the type hierarchy (i.e. casting a pointer to Foo
+// to a pointer to SuperclassOfFoo or casting a pointer to Foo to
+// a const pointer to Foo).
+// When you use implicit_cast, the compiler checks that the cast is safe.
+// Such explicit implicit_casts are necessary in surprisingly many
+// situations where C++ demands an exact type match instead of an
+// argument type convertible to a target type.
+//
+// The From type can be inferred, so the preferred syntax for using
+// implicit_cast is the same as for static_cast etc.:
+//
+// implicit_cast<ToType>(expr)
+//
+// implicit_cast would have been part of the C++ standard library,
+// but the proposal was submitted too late. It will probably make
+// its way into the language in the future.
+template<typename To, typename From>
+inline To implicit_cast(From const &f) {
+ return f;
+}
+
+// The COMPILE_ASSERT macro can be used to verify that a compile time
+// expression is true. For example, you could use it to verify the
+// size of a static array:
+//
+// COMPILE_ASSERT(ARRAYSIZE_UNSAFE(content_type_names) == CONTENT_NUM_TYPES,
+// content_type_names_incorrect_size);
+//
+// or to make sure a struct is smaller than a certain size:
+//
+// COMPILE_ASSERT(sizeof(foo) < 128, foo_too_large);
+//
+// The second argument to the macro is the name of the variable. If
+// the expression is false, most compilers will issue a warning/error
+// containing the name of the variable.
+
+#undef COMPILE_ASSERT
+#define COMPILE_ASSERT(expr, msg) static_assert(expr, #msg)
+
+// bit_cast<Dest,Source> is a template function that implements the
+// equivalent of "*reinterpret_cast<Dest*>(&source)". We need this in
+// very low-level functions like the protobuf library and fast math
+// support.
+//
+// float f = 3.14159265358979;
+// int i = bit_cast<int32>(f);
+// // i = 0x40490fdb
+//
+// The classical address-casting method is:
+//
+// // WRONG
+// float f = 3.14159265358979; // WRONG
+// int i = * reinterpret_cast<int*>(&f); // WRONG
+//
+// The address-casting method actually produces undefined behavior
+// according to ISO C++ specification section 3.10 -15 -. Roughly, this
+// section says: if an object in memory has one type, and a program
+// accesses it with a different type, then the result is undefined
+// behavior for most values of "different type".
+//
+// This is true for any cast syntax, either *(int*)&f or
+// *reinterpret_cast<int*>(&f). And it is particularly true for
+// conversions between integral lvalues and floating-point lvalues.
+//
+// The purpose of 3.10 -15- is to allow optimizing compilers to assume
+// that expressions with different types refer to different memory. gcc
+// 4.0.1 has an optimizer that takes advantage of this. So a
+// non-conforming program quietly produces wildly incorrect output.
+//
+// The problem is not the use of reinterpret_cast. The problem is type
+// punning: holding an object in memory of one type and reading its bits
+// back using a different type.
+//
+// The C++ standard is more subtle and complex than this, but that
+// is the basic idea.
+//
+// Anyways ...
+//
+// bit_cast<> calls memcpy() which is blessed by the standard,
+// especially by the example in section 3.9 . Also, of course,
+// bit_cast<> wraps up the nasty logic in one place.
+//
+// Fortunately memcpy() is very fast. In optimized mode, with a
+// constant size, gcc 2.95.3, gcc 4.0.1, and msvc 7.1 produce inline
+// code with the minimal amount of data movement. On a 32-bit system,
+// memcpy(d,s,4) compiles to one load and one store, and memcpy(d,s,8)
+// compiles to two loads and two stores.
+//
+// I tested this code with gcc 2.95.3, gcc 4.0.1, icc 8.1, and msvc 7.1.
+//
+// WARNING: if Dest or Source is a non-POD type, the result of the memcpy
+// is likely to surprise you.
+
+template <class Dest, class Source>
+inline Dest bit_cast(const Source& source) {
+ COMPILE_ASSERT(sizeof(Dest) == sizeof(Source), VerifySizesAreEqual);
+
+ Dest dest;
+ memcpy(&dest, &source, sizeof(dest));
+ return dest;
+}
+
+// Used to explicitly mark the return value of a function as unused. If you are
+// really sure you don't want to do anything with the return value of a function
+// that has been marked WARN_UNUSED_RESULT, wrap it with this. Example:
+//
+// scoped_ptr<MyType> my_var = ...;
+// if (TakeOwnership(my_var.get()) == SUCCESS)
+// ignore_result(my_var.release());
+//
+template<typename T>
+inline void ignore_result(const T&) {
+}
+
+// The following enum should be used only as a constructor argument to indicate
+// that the variable has static storage class, and that the constructor should
+// do nothing to its state. It indicates to the reader that it is legal to
+// declare a static instance of the class, provided the constructor is given
+// the base::LINKER_INITIALIZED argument. Normally, it is unsafe to declare a
+// static variable that has a constructor or a destructor because invocation
+// order is undefined. However, IF the type can be initialized by filling with
+// zeroes (which the loader does for static variables), AND the destructor also
+// does nothing to the storage, AND there are no virtual methods, then a
+// constructor declared as
+// explicit MyClass(base::LinkerInitialized x) {}
+// and invoked as
+// static MyClass my_variable_name(base::LINKER_INITIALIZED);
+namespace base {
+enum LinkerInitialized { LINKER_INITIALIZED };
+
+// Use these to declare and define a static local variable (static T;) so that
+// it is leaked so that its destructors are not called at exit. If you need
+// thread-safe initialization, use base/lazy_instance.h instead.
+#define CR_DEFINE_STATIC_LOCAL(type, name, arguments) \
+ static type& name = *new type arguments
+
+} // base
+
+#endif // BASE_MACROS_H_
diff --git a/perfprofd/quipper/base/port.h b/perfprofd/quipper/base/port.h
new file mode 100644
index 00000000..58f4969b
--- /dev/null
+++ b/perfprofd/quipper/base/port.h
@@ -0,0 +1,48 @@
+// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef BASE_PORT_H_
+#define BASE_PORT_H_
+
+#include <stdarg.h>
+#include "quipper/build/build_config.h"
+
+// DEPRECATED: Use ...LL and ...ULL suffixes.
+// TODO(viettrungluu): Delete these. These are only here until |GG_(U)INT64_C|
+// are deleted (some other header files (re)define |GG_(U)INT64_C|, so our
+// definitions of them must exactly match theirs).
+#ifdef COMPILER_MSVC
+#define GG_LONGLONG(x) x##I64
+#define GG_ULONGLONG(x) x##UI64
+#else
+#define GG_LONGLONG(x) x##LL
+#define GG_ULONGLONG(x) x##ULL
+#endif
+
+// DEPRECATED: In Chromium, we force-define __STDC_CONSTANT_MACROS, so you can
+// just use the regular (U)INTn_C macros from <stdint.h>.
+// TODO(viettrungluu): Remove the remaining GG_(U)INTn_C macros.
+#define GG_INT64_C(x) GG_LONGLONG(x)
+#define GG_UINT64_C(x) GG_ULONGLONG(x)
+
+// It's possible for functions that use a va_list, such as StringPrintf, to
+// invalidate the data in it upon use. The fix is to make a copy of the
+// structure before using it and use that copy instead. va_copy is provided
+// for this purpose. MSVC does not provide va_copy, so define an
+// implementation here. It is not guaranteed that assignment is a copy, so the
+// StringUtil.VariableArgsFunc unit test tests this capability.
+#if defined(COMPILER_GCC)
+#define GG_VA_COPY(a, b) (va_copy(a, b))
+#elif defined(COMPILER_MSVC)
+#define GG_VA_COPY(a, b) (a = b)
+#endif
+
+// Define an OS-neutral wrapper for shared library entry points
+#if defined(OS_WIN)
+#define API_CALL __stdcall
+#else
+#define API_CALL
+#endif
+
+#endif // BASE_PORT_H_
diff --git a/perfprofd/quipper/build/build_config.h b/perfprofd/quipper/build/build_config.h
new file mode 100644
index 00000000..55348460
--- /dev/null
+++ b/perfprofd/quipper/build/build_config.h
@@ -0,0 +1,159 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// This file adds defines about the platform we're currently building on.
+// Operating System:
+// OS_WIN / OS_MACOSX / OS_LINUX / OS_POSIX (MACOSX or LINUX) / OS_NACL
+// Compiler:
+// COMPILER_MSVC / COMPILER_GCC
+// Processor:
+// ARCH_CPU_X86 / ARCH_CPU_X86_64 / ARCH_CPU_X86_FAMILY (X86 or X86_64)
+// ARCH_CPU_32_BITS / ARCH_CPU_64_BITS
+
+#ifndef BUILD_BUILD_CONFIG_H_
+#define BUILD_BUILD_CONFIG_H_
+
+// A set of macros to use for platform detection.
+#if defined(__native_client__)
+// __native_client__ must be first, so that other OS_ defines are not set.
+#define OS_NACL 1
+#elif defined(ANDROID)
+#define OS_ANDROID 1
+#elif defined(__APPLE__)
+// only include TargetConditions after testing ANDROID as some android builds
+// on mac don't have this header available and it's not needed unless the target
+// is really mac/ios.
+#include <TargetConditionals.h>
+#define OS_MACOSX 1
+#if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
+#define OS_IOS 1
+#endif // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE
+#elif defined(__linux__)
+#define OS_LINUX 1
+// include a system header to pull in features.h for glibc/uclibc macros.
+#include <unistd.h>
+#if defined(__GLIBC__) && !defined(__UCLIBC__)
+// we really are using glibc, not uClibc pretending to be glibc
+#define LIBC_GLIBC 1
+#endif
+#elif defined(_WIN32)
+#define OS_WIN 1
+#define TOOLKIT_VIEWS 1
+#elif defined(__FreeBSD__)
+#define OS_FREEBSD 1
+#elif defined(__OpenBSD__)
+#define OS_OPENBSD 1
+#elif defined(__sun)
+#define OS_SOLARIS 1
+#elif defined(__QNXNTO__)
+#define OS_QNX 1
+#else
+#error Please add support for your platform in build/build_config.h
+#endif
+
+#if defined(USE_OPENSSL) && defined(USE_NSS)
+#error Cannot use both OpenSSL and NSS
+#endif
+
+// For access to standard BSD features, use OS_BSD instead of a
+// more specific macro.
+#if defined(OS_FREEBSD) || defined(OS_OPENBSD)
+#define OS_BSD 1
+#endif
+
+// For access to standard POSIXish features, use OS_POSIX instead of a
+// more specific macro.
+#if defined(OS_MACOSX) || defined(OS_LINUX) || defined(OS_FREEBSD) || \
+ defined(OS_OPENBSD) || defined(OS_SOLARIS) || defined(OS_ANDROID) || \
+ defined(OS_NACL) || defined(OS_QNX)
+#define OS_POSIX 1
+#endif
+
+// Use tcmalloc
+#if (defined(OS_WIN) || defined(OS_LINUX) || defined(OS_ANDROID)) && \
+ !defined(NO_TCMALLOC)
+#define USE_TCMALLOC 1
+#endif
+
+// Compiler detection.
+#if defined(__GNUC__)
+#define COMPILER_GCC 1
+#elif defined(_MSC_VER)
+#define COMPILER_MSVC 1
+#else
+#error Please add support for your compiler in build/build_config.h
+#endif
+
+// Processor architecture detection. For more info on what's defined, see:
+// http://msdn.microsoft.com/en-us/library/b0084kay.aspx
+// http://www.agner.org/optimize/calling_conventions.pdf
+// or with gcc, run: "echo | gcc -E -dM -"
+#if defined(_M_X64) || defined(__x86_64__)
+#define ARCH_CPU_X86_FAMILY 1
+#define ARCH_CPU_X86_64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(_M_IX86) || defined(__i386__)
+#define ARCH_CPU_X86_FAMILY 1
+#define ARCH_CPU_X86 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__ARMEL__)
+#define ARCH_CPU_ARM_FAMILY 1
+#define ARCH_CPU_ARMEL 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__aarch64__)
+#define ARCH_CPU_ARM_FAMILY 1
+#define ARCH_CPU_ARM64 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__pnacl__)
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#elif defined(__MIPSEL__)
+#if defined(__LP64__)
+#define ARCH_CPU_MIPS64_FAMILY 1
+#define ARCH_CPU_MIPS64EL 1
+#define ARCH_CPU_64_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#else
+#define ARCH_CPU_MIPS_FAMILY 1
+#define ARCH_CPU_MIPSEL 1
+#define ARCH_CPU_32_BITS 1
+#define ARCH_CPU_LITTLE_ENDIAN 1
+#endif
+#else
+#error Please add support for your architecture in build/build_config.h
+#endif
+
+// Type detection for wchar_t.
+#if defined(OS_WIN)
+#define WCHAR_T_IS_UTF16
+#elif defined(OS_POSIX) && defined(COMPILER_GCC) && \
+ defined(__WCHAR_MAX__) && \
+ (__WCHAR_MAX__ == 0x7fffffff || __WCHAR_MAX__ == 0xffffffff)
+#define WCHAR_T_IS_UTF32
+#elif defined(OS_POSIX) && defined(COMPILER_GCC) && \
+ defined(__WCHAR_MAX__) && \
+ (__WCHAR_MAX__ == 0x7fff || __WCHAR_MAX__ == 0xffff)
+// On Posix, we'll detect short wchar_t, but projects aren't guaranteed to
+// compile in this mode (in particular, Chrome doesn't). This is intended for
+// other projects using base who manage their own dependencies and make sure
+// short wchar works for them.
+#define WCHAR_T_IS_UTF16
+#else
+#error Please add support for your compiler in build/build_config.h
+#endif
+
+#if defined(OS_ANDROID)
+// The compiler thinks std::string::const_iterator and "const char*" are
+// equivalent types.
+#define STD_STRING_ITERATOR_IS_CHAR_POINTER
+// The compiler thinks base::string16::const_iterator and "char16*" are
+// equivalent types.
+#define BASE_STRING16_ITERATOR_IS_CHAR16_POINTER
+#endif
+
+#endif // BUILD_BUILD_CONFIG_H_
diff --git a/perfprofd/quipper/kernel-headers/tools/perf/perf.h b/perfprofd/quipper/kernel-headers/tools/perf/perf.h
new file mode 100644
index 00000000..e58da9ae
--- /dev/null
+++ b/perfprofd/quipper/kernel-headers/tools/perf/perf.h
@@ -0,0 +1,196 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** To edit the content of this header, modify the corresponding
+ *** source file (e.g. under external/kernel-headers/original/) then
+ *** run bionic/libc/kernel/tools/update_all.py
+ ***
+ *** Any manual change here will be lost the next time this script will
+ *** be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _PERF_PERF_H
+#define _PERF_PERF_H
+#ifdef __i386__
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define rmb() asm volatile("lock; addl $0,0(%%esp)" : : : "memory")
+#define cpu_relax() asm volatile("rep; nop" : : : "memory");
+#define CPUINFO_PROC "model name"
+#ifndef __NR_perf_event_open
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define __NR_perf_event_open 336
+#endif
+#endif
+#ifdef __x86_64__
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define rmb() asm volatile("lfence" : : : "memory")
+#define cpu_relax() asm volatile("rep; nop" : : : "memory");
+#define CPUINFO_PROC "model name"
+#ifndef __NR_perf_event_open
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define __NR_perf_event_open 298
+#endif
+#endif
+#ifdef __powerpc__
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define rmb() asm volatile("sync" : : : "memory")
+#define cpu_relax() asm volatile("" : : : "memory");
+#define CPUINFO_PROC "cpu"
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#endif
+#ifdef __s390__
+#define rmb() asm volatile("bcr 15,0" : : : "memory")
+#define cpu_relax() asm volatile("" : : : "memory");
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#endif
+#ifdef __sh__
+#if defined(__SH4A__) || defined(__SH5__)
+#define rmb() asm volatile("synco" : : : "memory")
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#else
+#define rmb() asm volatile("" : : : "memory")
+#endif
+#define cpu_relax() asm volatile("" : : : "memory")
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define CPUINFO_PROC "cpu type"
+#endif
+#ifdef __hppa__
+#define rmb() asm volatile("" : : : "memory")
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define cpu_relax() asm volatile("" : : : "memory");
+#define CPUINFO_PROC "cpu"
+#endif
+#ifdef __sparc__
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define rmb() asm volatile("" : : : "memory")
+#define cpu_relax() asm volatile("" : : : "memory")
+#define CPUINFO_PROC "cpu"
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#ifdef __alpha__
+#define rmb() asm volatile("mb" : : : "memory")
+#define cpu_relax() asm volatile("" : : : "memory")
+#define CPUINFO_PROC "cpu model"
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#endif
+#ifdef __ia64__
+#define rmb() asm volatile("mf" : : : "memory")
+#define cpu_relax() asm volatile("hint @pause" : : : "memory")
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define CPUINFO_PROC "model name"
+#endif
+#ifdef __arm__
+#define rmb() ((void(*) (void)) 0xffff0fa0) ()
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define cpu_relax() asm volatile("" : : : "memory")
+#define CPUINFO_PROC "Processor"
+#endif
+#ifdef __aarch64__
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define rmb() asm volatile("dmb ld" : : : "memory")
+#define cpu_relax() asm volatile("yield" : : : "memory")
+#endif
+#ifdef __mips__
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define rmb() asm volatile(".set mips2\n\t" "sync\n\t" ".set mips0" : : : "memory")
+#define cpu_relax() asm volatile("" : : : "memory")
+#define CPUINFO_PROC "cpu model"
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#ifdef __arc__
+#define rmb() asm volatile("" : : : "memory")
+#define cpu_relax() rmb()
+#define CPUINFO_PROC "Processor"
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#endif
+#ifdef __metag__
+#define rmb() asm volatile("" : : : "memory")
+#define cpu_relax() asm volatile("" : : : "memory")
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define CPUINFO_PROC "CPU"
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define PR_TASK_PERF_EVENTS_DISABLE 31
+#define PR_TASK_PERF_EVENTS_ENABLE 32
+#ifndef NSEC_PER_SEC
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define NSEC_PER_SEC 1000000000ULL
+#endif
+#ifndef NSEC_PER_USEC
+#define NSEC_PER_USEC 1000ULL
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#endif
+#define __user
+#define asmlinkage
+#define unlikely(x) __builtin_expect(! ! (x), 0)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define min(x,y) ({ typeof(x) _min1 = (x); typeof(y) _min2 = (y); (void) (& _min1 == & _min2); _min1 < _min2 ? _min1 : _min2; })
+#define MAX_COUNTERS 256
+#define MAX_NR_CPUS 256
+struct ip_callchain {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ u64 nr;
+ u64 ips[0];
+};
+struct branch_flags {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ u64 mispred : 1;
+ u64 predicted : 1;
+ u64 reserved : 62;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct branch_entry {
+ u64 from;
+ u64 to;
+ struct branch_flags flags;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct branch_stack {
+ u64 nr;
+ struct branch_entry entries[0];
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+enum perf_call_graph_mode {
+ CALLCHAIN_NONE,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ CALLCHAIN_FP,
+ CALLCHAIN_DWARF
+};
+struct perf_record_opts {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ struct perf_target target;
+ int call_graph;
+ bool group;
+ bool inherit_stat;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ bool no_delay;
+ bool no_inherit;
+ bool no_samples;
+ bool pipe_output;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ bool raw_samples;
+ bool sample_address;
+ bool sample_weight;
+ bool sample_time;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ bool period;
+ unsigned int freq;
+ unsigned int mmap_pages;
+ unsigned int user_freq;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ u64 branch_stack;
+ u64 default_interval;
+ u64 user_interval;
+ u16 stack_dump_size;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+#endif
+
diff --git a/perfprofd/quipper/kernel-headers/tools/perf/util/build-id.h b/perfprofd/quipper/kernel-headers/tools/perf/util/build-id.h
new file mode 100644
index 00000000..b7dbc166
--- /dev/null
+++ b/perfprofd/quipper/kernel-headers/tools/perf/util/build-id.h
@@ -0,0 +1,25 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** To edit the content of this header, modify the corresponding
+ *** source file (e.g. under external/kernel-headers/original/) then
+ *** run bionic/libc/kernel/tools/update_all.py
+ ***
+ *** Any manual change here will be lost the next time this script will
+ *** be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef PERF_BUILD_ID_H_
+#define PERF_BUILD_ID_H_ 1
+#define BUILD_ID_SIZE 20
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct dso;
+#endif
+
diff --git a/perfprofd/quipper/kernel-headers/tools/perf/util/event.h b/perfprofd/quipper/kernel-headers/tools/perf/util/event.h
new file mode 100644
index 00000000..00283447
--- /dev/null
+++ b/perfprofd/quipper/kernel-headers/tools/perf/util/event.h
@@ -0,0 +1,204 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** To edit the content of this header, modify the corresponding
+ *** source file (e.g. under external/kernel-headers/original/) then
+ *** run bionic/libc/kernel/tools/update_all.py
+ ***
+ *** Any manual change here will be lost the next time this script will
+ *** be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef __PERF_RECORD_H
+#define __PERF_RECORD_H
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct mmap_event {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ struct perf_event_header header;
+ u32 pid, tid;
+ u64 start;
+ u64 len;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ u64 pgoff;
+ char filename[PATH_MAX];
+};
+struct mmap2_event {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ struct perf_event_header header;
+ u32 pid, tid;
+ u64 start;
+ u64 len;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ u64 pgoff;
+ u32 maj;
+ u32 min;
+ u64 ino;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ u64 ino_generation;
+ char filename[PATH_MAX];
+};
+struct comm_event {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ struct perf_event_header header;
+ u32 pid, tid;
+ char comm[16];
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct fork_event {
+ struct perf_event_header header;
+ u32 pid, ppid;
+ u32 tid, ptid;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ u64 time;
+};
+struct lost_event {
+ struct perf_event_header header;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ u64 id;
+ u64 lost;
+};
+struct read_event {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ struct perf_event_header header;
+ u32 pid, tid;
+ u64 value;
+ u64 time_enabled;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ u64 time_running;
+ u64 id;
+};
+#define PERF_SAMPLE_MASK (PERF_SAMPLE_IP | PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_ADDR | PERF_SAMPLE_ID | PERF_SAMPLE_STREAM_ID | PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD | PERF_SAMPLE_IDENTIFIER)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct sample_event {
+ struct perf_event_header header;
+ u64 array[];
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct regs_dump {
+ u64 abi;
+ u64 * regs;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct stack_dump {
+ u16 offset;
+ u64 size;
+ char * data;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct sample_read_value {
+ u64 value;
+ u64 id;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct sample_read {
+ u64 time_enabled;
+ u64 time_running;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ union {
+ struct {
+ u64 nr;
+ struct sample_read_value * values;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ } group;
+ struct sample_read_value one;
+ };
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct perf_sample {
+ u64 ip;
+ u32 pid, tid;
+ u64 time;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ u64 addr;
+ u64 id;
+ u64 stream_id;
+ u64 period;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ u64 weight;
+ u32 cpu;
+ u32 raw_size;
+ u64 data_src;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ void * raw_data;
+ struct ip_callchain * callchain;
+ struct branch_stack * branch_stack;
+ struct regs_dump user_regs;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ struct stack_dump user_stack;
+ struct sample_read read;
+};
+#define PERF_MEM_DATA_SRC_NONE (PERF_MEM_S(OP, NA) | PERF_MEM_S(LVL, NA) | PERF_MEM_S(SNOOP, NA) | PERF_MEM_S(LOCK, NA) | PERF_MEM_S(TLB, NA))
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct build_id_event {
+ struct perf_event_header header;
+ pid_t pid;
+ u8 build_id[PERF_ALIGN(BUILD_ID_SIZE, sizeof(u64))];
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ char filename[];
+};
+enum perf_user_event_type {
+ PERF_RECORD_USER_TYPE_START = 64,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ PERF_RECORD_HEADER_ATTR = 64,
+ PERF_RECORD_HEADER_EVENT_TYPE = 65,
+ PERF_RECORD_HEADER_TRACING_DATA = 66,
+ PERF_RECORD_HEADER_BUILD_ID = 67,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ PERF_RECORD_FINISHED_ROUND = 68,
+ PERF_RECORD_HEADER_MAX
+};
+struct attr_event {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ struct perf_event_header header;
+ struct perf_event_attr attr;
+ u64 id[];
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define MAX_EVENT_NAME 64
+struct perf_trace_event_type {
+ u64 event_id;
+ char name[MAX_EVENT_NAME];
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct event_type_event {
+ struct perf_event_header header;
+ struct perf_trace_event_type event_type;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct tracing_data_event {
+ struct perf_event_header header;
+ u32 size;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+union perf_event {
+ struct perf_event_header header;
+ struct mmap_event mmap;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ struct mmap2_event mmap2;
+ struct comm_event comm;
+ struct fork_event fork;
+ struct lost_event lost;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ struct read_event read;
+ struct sample_event sample;
+ struct attr_event attr;
+ struct event_type_event event_type;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ struct tracing_data_event tracing_data;
+ struct build_id_event build_id;
+};
+struct perf_tool;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct thread_map;
+typedef int(* perf_event__handler_t) (struct perf_tool * tool, union perf_event * event, struct perf_sample * sample, struct machine * machine);
+struct addr_location;
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+
diff --git a/perfprofd/quipper/kernel-headers/tools/perf/util/header.h b/perfprofd/quipper/kernel-headers/tools/perf/util/header.h
new file mode 100644
index 00000000..3aab42fd
--- /dev/null
+++ b/perfprofd/quipper/kernel-headers/tools/perf/util/header.h
@@ -0,0 +1,121 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** To edit the content of this header, modify the corresponding
+ *** source file (e.g. under external/kernel-headers/original/) then
+ *** run bionic/libc/kernel/tools/update_all.py
+ ***
+ *** Any manual change here will be lost the next time this script will
+ *** be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef __PERF_HEADER_H
+#define __PERF_HEADER_H
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+enum {
+ HEADER_RESERVED = 0,
+ HEADER_FIRST_FEATURE = 1,
+ HEADER_TRACING_DATA = 1,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ HEADER_BUILD_ID,
+ HEADER_HOSTNAME,
+ HEADER_OSRELEASE,
+ HEADER_VERSION,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ HEADER_ARCH,
+ HEADER_NRCPUS,
+ HEADER_CPUDESC,
+ HEADER_CPUID,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ HEADER_TOTAL_MEM,
+ HEADER_CMDLINE,
+ HEADER_EVENT_DESC,
+ HEADER_CPU_TOPOLOGY,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ HEADER_NUMA_TOPOLOGY,
+ HEADER_BRANCH_STACK,
+ HEADER_PMU_MAPPINGS,
+ HEADER_GROUP_DESC,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ HEADER_LAST_FEATURE,
+ HEADER_FEAT_BITS = 256,
+};
+enum perf_header_version {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ PERF_HEADER_VERSION_1,
+ PERF_HEADER_VERSION_2,
+};
+struct perf_file_section {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ u64 offset;
+ u64 size;
+};
+struct perf_file_header {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ u64 magic;
+ u64 size;
+ u64 attr_size;
+ struct perf_file_section attrs;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ struct perf_file_section data;
+ struct perf_file_section event_types;
+ DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct perf_pipe_file_header {
+ u64 magic;
+ u64 size;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct perf_header;
+struct perf_session_env {
+ char * hostname;
+ char * os_release;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ char * version;
+ char * arch;
+ int nr_cpus_online;
+ int nr_cpus_avail;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ char * cpu_desc;
+ char * cpuid;
+ unsigned long long total_mem;
+ int nr_cmdline;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ char * cmdline;
+ int nr_sibling_cores;
+ char * sibling_cores;
+ int nr_sibling_threads;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ char * sibling_threads;
+ int nr_numa_nodes;
+ char * numa_nodes;
+ int nr_pmu_mappings;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ char * pmu_mappings;
+ int nr_groups;
+};
+struct perf_header {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ enum perf_header_version version;
+ bool needs_swap;
+ u64 data_offset;
+ u64 data_size;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ u64 feat_offset;
+ DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
+ struct perf_session_env env;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct perf_evlist;
+struct perf_session;
+#endif
+
diff --git a/perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/bitops.h b/perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/bitops.h
new file mode 100644
index 00000000..c6c47683
--- /dev/null
+++ b/perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/bitops.h
@@ -0,0 +1,41 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** To edit the content of this header, modify the corresponding
+ *** source file (e.g. under external/kernel-headers/original/) then
+ *** run bionic/libc/kernel/tools/update_all.py
+ ***
+ *** Any manual change here will be lost the next time this script will
+ *** be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _PERF_LINUX_BITOPS_H_
+#define _PERF_LINUX_BITOPS_H_
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#ifndef __WORDSIZE
+#define __WORDSIZE (__SIZEOF_LONG__ * 8)
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define BITS_PER_LONG __WORDSIZE
+#define BITS_PER_BYTE 8
+#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
+#define BITS_TO_U64(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64))
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32))
+#define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE)
+#define for_each_set_bit(bit,addr,size) for((bit) = find_first_bit((addr), (size)); (bit) < (size); (bit) = find_next_bit((addr), (size), (bit) + 1))
+#define for_each_set_bit_from(bit,addr,size) for((bit) = find_next_bit((addr), (size), (bit)); (bit) < (size); (bit) = find_next_bit((addr), (size), (bit) + 1))
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG)
+#if BITS_PER_LONG == 64
+#endif
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+
diff --git a/perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/kernel/kernel.h b/perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/kernel/kernel.h
new file mode 100644
index 00000000..d589c85d
--- /dev/null
+++ b/perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/kernel/kernel.h
@@ -0,0 +1,79 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** To edit the content of this header, modify the corresponding
+ *** source file (e.g. under external/kernel-headers/original/) then
+ *** run bionic/libc/kernel/tools/update_all.py
+ ***
+ *** Any manual change here will be lost the next time this script will
+ *** be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef PERF_LINUX_KERNEL_H_
+#define PERF_LINUX_KERNEL_H_
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
+#define PERF_ALIGN(x,a) __PERF_ALIGN_MASK(x, (typeof(x)) (a) - 1)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define __PERF_ALIGN_MASK(x,mask) (((x) + (mask)) & ~(mask))
+#ifndef offsetof
+#define offsetof(TYPE,MEMBER) ((size_t) & ((TYPE *) 0)->MEMBER)
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#ifndef container_of
+#define container_of(ptr,type,member) ({ const typeof(((type *) 0)->member) * __mptr = (ptr); (type *) ((char *) __mptr - offsetof(type, member)); })
+#endif
+#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int : - ! ! (e); }))
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#ifndef max
+#define max(x,y) ({ typeof(x) _max1 = (x); typeof(y) _max2 = (y); (void) (& _max1 == & _max2); _max1 > _max2 ? _max1 : _max2; })
+#endif
+#ifndef min
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define min(x,y) ({ typeof(x) _min1 = (x); typeof(y) _min2 = (y); (void) (& _min1 == & _min2); _min1 < _min2 ? _min1 : _min2; })
+#endif
+#ifndef roundup
+#define roundup(x,y) (\
+{ const typeof(y) __y = y; (((x) + (__y - 1)) / __y) * __y; \
+} \
+)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#endif
+#ifndef BUG_ON
+#ifdef NDEBUG
+#define BUG_ON(cond) do { if(cond) { } } while(0)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#else
+#define BUG_ON(cond) assert(! (cond))
+#endif
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define cpu_to_le64(x) (x)
+#define cpu_to_le32(x) (x)
+#ifndef pr_fmt
+#define pr_fmt(fmt) fmt
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#endif
+#define pr_err(fmt,...) eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_warning(fmt,...) eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_info(fmt,...) eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define pr_debug(fmt,...) eprintf(1, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debugN(n,fmt,...) eprintf(n, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debug2(fmt,...) pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debug3(fmt,...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__)
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define pr_debug4(fmt,...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__)
+#define __round_mask(x,y) ((__typeof__(x)) ((y) - 1))
+#define round_up(x,y) ((((x) - 1) | __round_mask(x, y)) + 1)
+#define round_down(x,y) ((x) & ~__round_mask(x, y))
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#endif
+
diff --git a/perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/types.h b/perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/types.h
new file mode 100644
index 00000000..2ac27995
--- /dev/null
+++ b/perfprofd/quipper/kernel-headers/tools/perf/util/include/linux/types.h
@@ -0,0 +1,43 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** To edit the content of this header, modify the corresponding
+ *** source file (e.g. under external/kernel-headers/original/) then
+ *** run bionic/libc/kernel/tools/update_all.py
+ ***
+ *** Any manual change here will be lost the next time this script will
+ *** be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _PERF_LINUX_TYPES_H_
+#define _PERF_LINUX_TYPES_H_
+#ifndef __bitwise
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#define __bitwise
+#endif
+#ifndef __le32
+typedef __u32 __bitwise __le32;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+#endif
+#define DECLARE_BITMAP(name,bits) unsigned long name[BITS_TO_LONGS(bits)]
+struct list_head {
+ struct list_head * next, * prev;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+struct hlist_head {
+ struct hlist_node * first;
+};
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct hlist_node {
+ struct hlist_node * next, * * pprev;
+};
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+
diff --git a/perfprofd/quipper/kernel-headers/tools/perf/util/target.h b/perfprofd/quipper/kernel-headers/tools/perf/util/target.h
new file mode 100644
index 00000000..e6c3d949
--- /dev/null
+++ b/perfprofd/quipper/kernel-headers/tools/perf/util/target.h
@@ -0,0 +1,52 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** To edit the content of this header, modify the corresponding
+ *** source file (e.g. under external/kernel-headers/original/) then
+ *** run bionic/libc/kernel/tools/update_all.py
+ ***
+ *** Any manual change here will be lost the next time this script will
+ *** be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef _PERF_TARGET_H
+#define _PERF_TARGET_H
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+struct perf_target {
+ const char * pid;
+ const char * tid;
+ const char * cpu_list;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ const char * uid_str;
+ uid_t uid;
+ bool system_wide;
+ bool uses_mmap;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+enum perf_target_errno {
+ PERF_ERRNO_TARGET__SUCCESS = 0,
+ __PERF_ERRNO_TARGET__START = - 10000,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ PERF_ERRNO_TARGET__PID_OVERRIDE_CPU = __PERF_ERRNO_TARGET__START,
+ PERF_ERRNO_TARGET__PID_OVERRIDE_UID,
+ PERF_ERRNO_TARGET__UID_OVERRIDE_CPU,
+ PERF_ERRNO_TARGET__PID_OVERRIDE_SYSTEM,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM,
+ PERF_ERRNO_TARGET__INVALID_UID,
+ PERF_ERRNO_TARGET__USER_NOT_FOUND,
+ __PERF_ERRNO_TARGET__END,
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+};
+enum perf_target_errno perf_target__validate(struct perf_target * target);
+enum perf_target_errno perf_target__parse_uid(struct perf_target * target);
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+
diff --git a/perfprofd/quipper/kernel-headers/tools/perf/util/types.h b/perfprofd/quipper/kernel-headers/tools/perf/util/types.h
new file mode 100644
index 00000000..cf36814f
--- /dev/null
+++ b/perfprofd/quipper/kernel-headers/tools/perf/util/types.h
@@ -0,0 +1,38 @@
+/****************************************************************************
+ ****************************************************************************
+ ***
+ *** This header was automatically generated from a Linux kernel header
+ *** of the same name, to make information necessary for userspace to
+ *** call into the kernel available to libc. It contains only constants,
+ *** structures, and macros generated from the original header, and thus,
+ *** contains no copyrightable information.
+ ***
+ *** To edit the content of this header, modify the corresponding
+ *** source file (e.g. under external/kernel-headers/original/) then
+ *** run bionic/libc/kernel/tools/update_all.py
+ ***
+ *** Any manual change here will be lost the next time this script will
+ *** be run. You've been warned!
+ ***
+ ****************************************************************************
+ ****************************************************************************/
+#ifndef __PERF_TYPES_H
+#define __PERF_TYPES_H
+typedef uint64_t u64;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+typedef int64_t s64;
+typedef unsigned int u32;
+typedef signed int s32;
+typedef unsigned short u16;
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+typedef signed short s16;
+typedef unsigned char u8;
+typedef signed char s8;
+union u64_swap {
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+ u64 val64;
+ u32 val32[2];
+};
+#endif
+/* WARNING: DO NOT EDIT, AUTO-GENERATED CODE - SEE TOP FOR INSTRUCTIONS */
+
diff --git a/perfprofd/quipper/original-kernel-headers/tools/perf/perf.h b/perfprofd/quipper/original-kernel-headers/tools/perf/perf.h
new file mode 100644
index 00000000..cf20187e
--- /dev/null
+++ b/perfprofd/quipper/original-kernel-headers/tools/perf/perf.h
@@ -0,0 +1,236 @@
+#ifndef _PERF_PERF_H
+#define _PERF_PERF_H
+
+#include <asm/unistd.h>
+
+#if defined(__i386__)
+#define rmb() asm volatile("lock; addl $0,0(%%esp)" ::: "memory")
+#define cpu_relax() asm volatile("rep; nop" ::: "memory");
+#define CPUINFO_PROC "model name"
+#ifndef __NR_perf_event_open
+# define __NR_perf_event_open 336
+#endif
+#endif
+
+#if defined(__x86_64__)
+#define rmb() asm volatile("lfence" ::: "memory")
+#define cpu_relax() asm volatile("rep; nop" ::: "memory");
+#define CPUINFO_PROC "model name"
+#ifndef __NR_perf_event_open
+# define __NR_perf_event_open 298
+#endif
+#endif
+
+#ifdef __powerpc__
+#include "../../arch/powerpc/include/uapi/asm/unistd.h"
+#define rmb() asm volatile ("sync" ::: "memory")
+#define cpu_relax() asm volatile ("" ::: "memory");
+#define CPUINFO_PROC "cpu"
+#endif
+
+#ifdef __s390__
+#define rmb() asm volatile("bcr 15,0" ::: "memory")
+#define cpu_relax() asm volatile("" ::: "memory");
+#endif
+
+#ifdef __sh__
+#if defined(__SH4A__) || defined(__SH5__)
+# define rmb() asm volatile("synco" ::: "memory")
+#else
+# define rmb() asm volatile("" ::: "memory")
+#endif
+#define cpu_relax() asm volatile("" ::: "memory")
+#define CPUINFO_PROC "cpu type"
+#endif
+
+#ifdef __hppa__
+#define rmb() asm volatile("" ::: "memory")
+#define cpu_relax() asm volatile("" ::: "memory");
+#define CPUINFO_PROC "cpu"
+#endif
+
+#ifdef __sparc__
+#define rmb() asm volatile("":::"memory")
+#define cpu_relax() asm volatile("":::"memory")
+#define CPUINFO_PROC "cpu"
+#endif
+
+#ifdef __alpha__
+#define rmb() asm volatile("mb" ::: "memory")
+#define cpu_relax() asm volatile("" ::: "memory")
+#define CPUINFO_PROC "cpu model"
+#endif
+
+#ifdef __ia64__
+#define rmb() asm volatile ("mf" ::: "memory")
+#define cpu_relax() asm volatile ("hint @pause" ::: "memory")
+#define CPUINFO_PROC "model name"
+#endif
+
+#ifdef __arm__
+/*
+ * Use the __kuser_memory_barrier helper in the CPU helper page. See
+ * arch/arm/kernel/entry-armv.S in the kernel source for details.
+ */
+#define rmb() ((void(*)(void))0xffff0fa0)()
+#define cpu_relax() asm volatile("":::"memory")
+#define CPUINFO_PROC "Processor"
+#endif
+
+#ifdef __aarch64__
+#define rmb() asm volatile("dmb ld" ::: "memory")
+#define cpu_relax() asm volatile("yield" ::: "memory")
+#endif
+
+#ifdef __mips__
+#define rmb() asm volatile( \
+ ".set mips2\n\t" \
+ "sync\n\t" \
+ ".set mips0" \
+ : /* no output */ \
+ : /* no input */ \
+ : "memory")
+#define cpu_relax() asm volatile("" ::: "memory")
+#define CPUINFO_PROC "cpu model"
+#endif
+
+#ifdef __arc__
+#define rmb() asm volatile("" ::: "memory")
+#define cpu_relax() rmb()
+#define CPUINFO_PROC "Processor"
+#endif
+
+#ifdef __metag__
+#define rmb() asm volatile("" ::: "memory")
+#define cpu_relax() asm volatile("" ::: "memory")
+#define CPUINFO_PROC "CPU"
+#endif
+
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+
+#include <linux/perf_event.h>
+#include "util/types.h"
+#include <stdbool.h>
+
+/*
+ * prctl(PR_TASK_PERF_EVENTS_DISABLE) will (cheaply) disable all
+ * counters in the current task.
+ */
+#define PR_TASK_PERF_EVENTS_DISABLE 31
+#define PR_TASK_PERF_EVENTS_ENABLE 32
+
+#ifndef NSEC_PER_SEC
+# define NSEC_PER_SEC 1000000000ULL
+#endif
+#ifndef NSEC_PER_USEC
+# define NSEC_PER_USEC 1000ULL
+#endif
+
+static inline unsigned long long rdclock(void)
+{
+ struct timespec ts;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ return ts.tv_sec * 1000000000ULL + ts.tv_nsec;
+}
+
+/*
+ * Pick up some kernel type conventions:
+ */
+#define __user
+#define asmlinkage
+
+#define unlikely(x) __builtin_expect(!!(x), 0)
+#define min(x, y) ({ \
+ typeof(x) _min1 = (x); \
+ typeof(y) _min2 = (y); \
+ (void) (&_min1 == &_min2); \
+ _min1 < _min2 ? _min1 : _min2; })
+
+extern bool test_attr__enabled;
+void test_attr__init(void);
+void test_attr__open(struct perf_event_attr *attr, pid_t pid, int cpu,
+ int fd, int group_fd, unsigned long flags);
+
+static inline int
+sys_perf_event_open(struct perf_event_attr *attr,
+ pid_t pid, int cpu, int group_fd,
+ unsigned long flags)
+{
+ int fd;
+
+ fd = syscall(__NR_perf_event_open, attr, pid, cpu,
+ group_fd, flags);
+
+ if (unlikely(test_attr__enabled))
+ test_attr__open(attr, pid, cpu, fd, group_fd, flags);
+
+ return fd;
+}
+
+#define MAX_COUNTERS 256
+#define MAX_NR_CPUS 256
+
+struct ip_callchain {
+ u64 nr;
+ u64 ips[0];
+};
+
+struct branch_flags {
+ u64 mispred:1;
+ u64 predicted:1;
+ u64 reserved:62;
+};
+
+struct branch_entry {
+ u64 from;
+ u64 to;
+ struct branch_flags flags;
+};
+
+struct branch_stack {
+ u64 nr;
+ struct branch_entry entries[0];
+};
+
+extern const char *input_name;
+extern bool perf_host, perf_guest;
+extern const char perf_version_string[];
+
+void pthread__unblock_sigwinch(void);
+
+#include "util/target.h"
+
+enum perf_call_graph_mode {
+ CALLCHAIN_NONE,
+ CALLCHAIN_FP,
+ CALLCHAIN_DWARF
+};
+
+struct perf_record_opts {
+ struct perf_target target;
+ int call_graph;
+ bool group;
+ bool inherit_stat;
+ bool no_delay;
+ bool no_inherit;
+ bool no_samples;
+ bool pipe_output;
+ bool raw_samples;
+ bool sample_address;
+ bool sample_weight;
+ bool sample_time;
+ bool period;
+ unsigned int freq;
+ unsigned int mmap_pages;
+ unsigned int user_freq;
+ u64 branch_stack;
+ u64 default_interval;
+ u64 user_interval;
+ u16 stack_dump_size;
+};
+
+#endif
diff --git a/perfprofd/quipper/original-kernel-headers/tools/perf/util/build-id.h b/perfprofd/quipper/original-kernel-headers/tools/perf/util/build-id.h
new file mode 100644
index 00000000..a811f5c6
--- /dev/null
+++ b/perfprofd/quipper/original-kernel-headers/tools/perf/util/build-id.h
@@ -0,0 +1,19 @@
+#ifndef PERF_BUILD_ID_H_
+#define PERF_BUILD_ID_H_ 1
+
+#define BUILD_ID_SIZE 20
+
+#include "tool.h"
+#include "types.h"
+
+extern struct perf_tool build_id__mark_dso_hit_ops;
+struct dso;
+
+int build_id__sprintf(const u8 *build_id, int len, char *bf);
+char *dso__build_id_filename(struct dso *self, char *bf, size_t size);
+
+int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event,
+ struct perf_sample *sample, struct perf_evsel *evsel,
+ struct machine *machine);
+
+#endif
diff --git a/perfprofd/quipper/original-kernel-headers/tools/perf/util/event.h b/perfprofd/quipper/original-kernel-headers/tools/perf/util/event.h
new file mode 100644
index 00000000..c67ecc45
--- /dev/null
+++ b/perfprofd/quipper/original-kernel-headers/tools/perf/util/event.h
@@ -0,0 +1,263 @@
+#ifndef __PERF_RECORD_H
+#define __PERF_RECORD_H
+
+#include <limits.h>
+#include <stdio.h>
+
+#include "../perf.h"
+#include "map.h"
+#include "build-id.h"
+
+struct mmap_event {
+ struct perf_event_header header;
+ u32 pid, tid;
+ u64 start;
+ u64 len;
+ u64 pgoff;
+ char filename[PATH_MAX];
+};
+
+struct mmap2_event {
+ struct perf_event_header header;
+ u32 pid, tid;
+ u64 start;
+ u64 len;
+ u64 pgoff;
+ u32 maj;
+ u32 min;
+ u64 ino;
+ u64 ino_generation;
+ char filename[PATH_MAX];
+};
+
+struct comm_event {
+ struct perf_event_header header;
+ u32 pid, tid;
+ char comm[16];
+};
+
+struct fork_event {
+ struct perf_event_header header;
+ u32 pid, ppid;
+ u32 tid, ptid;
+ u64 time;
+};
+
+struct lost_event {
+ struct perf_event_header header;
+ u64 id;
+ u64 lost;
+};
+
+/*
+ * PERF_FORMAT_ENABLED | PERF_FORMAT_RUNNING | PERF_FORMAT_ID
+ */
+struct read_event {
+ struct perf_event_header header;
+ u32 pid, tid;
+ u64 value;
+ u64 time_enabled;
+ u64 time_running;
+ u64 id;
+};
+
+
+#define PERF_SAMPLE_MASK \
+ (PERF_SAMPLE_IP | PERF_SAMPLE_TID | \
+ PERF_SAMPLE_TIME | PERF_SAMPLE_ADDR | \
+ PERF_SAMPLE_ID | PERF_SAMPLE_STREAM_ID | \
+ PERF_SAMPLE_CPU | PERF_SAMPLE_PERIOD | \
+ PERF_SAMPLE_IDENTIFIER)
+
+struct sample_event {
+ struct perf_event_header header;
+ u64 array[];
+};
+
+struct regs_dump {
+ u64 abi;
+ u64 *regs;
+};
+
+struct stack_dump {
+ u16 offset;
+ u64 size;
+ char *data;
+};
+
+struct sample_read_value {
+ u64 value;
+ u64 id;
+};
+
+struct sample_read {
+ u64 time_enabled;
+ u64 time_running;
+ union {
+ struct {
+ u64 nr;
+ struct sample_read_value *values;
+ } group;
+ struct sample_read_value one;
+ };
+};
+
+struct perf_sample {
+ u64 ip;
+ u32 pid, tid;
+ u64 time;
+ u64 addr;
+ u64 id;
+ u64 stream_id;
+ u64 period;
+ u64 weight;
+ u32 cpu;
+ u32 raw_size;
+ u64 data_src;
+ void *raw_data;
+ struct ip_callchain *callchain;
+ struct branch_stack *branch_stack;
+ struct regs_dump user_regs;
+ struct stack_dump user_stack;
+ struct sample_read read;
+};
+
+#define PERF_MEM_DATA_SRC_NONE \
+ (PERF_MEM_S(OP, NA) |\
+ PERF_MEM_S(LVL, NA) |\
+ PERF_MEM_S(SNOOP, NA) |\
+ PERF_MEM_S(LOCK, NA) |\
+ PERF_MEM_S(TLB, NA))
+
+struct build_id_event {
+ struct perf_event_header header;
+ pid_t pid;
+ u8 build_id[PERF_ALIGN(BUILD_ID_SIZE, sizeof(u64))];
+ char filename[];
+};
+
+enum perf_user_event_type { /* above any possible kernel type */
+ PERF_RECORD_USER_TYPE_START = 64,
+ PERF_RECORD_HEADER_ATTR = 64,
+ PERF_RECORD_HEADER_EVENT_TYPE = 65, /* depreceated */
+ PERF_RECORD_HEADER_TRACING_DATA = 66,
+ PERF_RECORD_HEADER_BUILD_ID = 67,
+ PERF_RECORD_FINISHED_ROUND = 68,
+ PERF_RECORD_HEADER_MAX
+};
+
+struct attr_event {
+ struct perf_event_header header;
+ struct perf_event_attr attr;
+ u64 id[];
+};
+
+#define MAX_EVENT_NAME 64
+
+struct perf_trace_event_type {
+ u64 event_id;
+ char name[MAX_EVENT_NAME];
+};
+
+struct event_type_event {
+ struct perf_event_header header;
+ struct perf_trace_event_type event_type;
+};
+
+struct tracing_data_event {
+ struct perf_event_header header;
+ u32 size;
+};
+
+union perf_event {
+ struct perf_event_header header;
+ struct mmap_event mmap;
+ struct mmap2_event mmap2;
+ struct comm_event comm;
+ struct fork_event fork;
+ struct lost_event lost;
+ struct read_event read;
+ struct sample_event sample;
+ struct attr_event attr;
+ struct event_type_event event_type;
+ struct tracing_data_event tracing_data;
+ struct build_id_event build_id;
+};
+
+void perf_event__print_totals(void);
+
+struct perf_tool;
+struct thread_map;
+
+typedef int (*perf_event__handler_t)(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct machine *machine);
+
+int perf_event__synthesize_thread_map(struct perf_tool *tool,
+ struct thread_map *threads,
+ perf_event__handler_t process,
+ struct machine *machine);
+int perf_event__synthesize_threads(struct perf_tool *tool,
+ perf_event__handler_t process,
+ struct machine *machine);
+int perf_event__synthesize_kernel_mmap(struct perf_tool *tool,
+ perf_event__handler_t process,
+ struct machine *machine,
+ const char *symbol_name);
+
+int perf_event__synthesize_modules(struct perf_tool *tool,
+ perf_event__handler_t process,
+ struct machine *machine);
+
+int perf_event__process_comm(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct machine *machine);
+int perf_event__process_lost(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct machine *machine);
+int perf_event__process_mmap(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct machine *machine);
+int perf_event__process_mmap2(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct machine *machine);
+int perf_event__process_fork(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct machine *machine);
+int perf_event__process_exit(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct machine *machine);
+int perf_event__process(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_sample *sample,
+ struct machine *machine);
+
+struct addr_location;
+int perf_event__preprocess_sample(const union perf_event *self,
+ struct machine *machine,
+ struct addr_location *al,
+ struct perf_sample *sample);
+
+const char *perf_event__name(unsigned int id);
+
+size_t perf_event__sample_event_size(const struct perf_sample *sample, u64 type,
+ u64 sample_regs_user, u64 read_format);
+int perf_event__synthesize_sample(union perf_event *event, u64 type,
+ u64 sample_regs_user, u64 read_format,
+ const struct perf_sample *sample,
+ bool swapped);
+
+size_t perf_event__fprintf_comm(union perf_event *event, FILE *fp);
+size_t perf_event__fprintf_mmap(union perf_event *event, FILE *fp);
+size_t perf_event__fprintf_mmap2(union perf_event *event, FILE *fp);
+size_t perf_event__fprintf_task(union perf_event *event, FILE *fp);
+size_t perf_event__fprintf(union perf_event *event, FILE *fp);
+
+#endif /* __PERF_RECORD_H */
diff --git a/perfprofd/quipper/original-kernel-headers/tools/perf/util/header.h b/perfprofd/quipper/original-kernel-headers/tools/perf/util/header.h
new file mode 100644
index 00000000..307c9aed
--- /dev/null
+++ b/perfprofd/quipper/original-kernel-headers/tools/perf/util/header.h
@@ -0,0 +1,159 @@
+#ifndef __PERF_HEADER_H
+#define __PERF_HEADER_H
+
+#include <linux/perf_event.h>
+#include <sys/types.h>
+#include <stdbool.h>
+#include "types.h"
+#include "event.h"
+
+#include <linux/bitmap.h>
+
+enum {
+ HEADER_RESERVED = 0, /* always cleared */
+ HEADER_FIRST_FEATURE = 1,
+ HEADER_TRACING_DATA = 1,
+ HEADER_BUILD_ID,
+
+ HEADER_HOSTNAME,
+ HEADER_OSRELEASE,
+ HEADER_VERSION,
+ HEADER_ARCH,
+ HEADER_NRCPUS,
+ HEADER_CPUDESC,
+ HEADER_CPUID,
+ HEADER_TOTAL_MEM,
+ HEADER_CMDLINE,
+ HEADER_EVENT_DESC,
+ HEADER_CPU_TOPOLOGY,
+ HEADER_NUMA_TOPOLOGY,
+ HEADER_BRANCH_STACK,
+ HEADER_PMU_MAPPINGS,
+ HEADER_GROUP_DESC,
+ HEADER_LAST_FEATURE,
+ HEADER_FEAT_BITS = 256,
+};
+
+enum perf_header_version {
+ PERF_HEADER_VERSION_1,
+ PERF_HEADER_VERSION_2,
+};
+
+struct perf_file_section {
+ u64 offset;
+ u64 size;
+};
+
+struct perf_file_header {
+ u64 magic;
+ u64 size;
+ u64 attr_size;
+ struct perf_file_section attrs;
+ struct perf_file_section data;
+ /* event_types is ignored */
+ struct perf_file_section event_types;
+ DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
+};
+
+struct perf_pipe_file_header {
+ u64 magic;
+ u64 size;
+};
+
+struct perf_header;
+
+int perf_file_header__read(struct perf_file_header *header,
+ struct perf_header *ph, int fd);
+
+struct perf_session_env {
+ char *hostname;
+ char *os_release;
+ char *version;
+ char *arch;
+ int nr_cpus_online;
+ int nr_cpus_avail;
+ char *cpu_desc;
+ char *cpuid;
+ unsigned long long total_mem;
+
+ int nr_cmdline;
+ char *cmdline;
+ int nr_sibling_cores;
+ char *sibling_cores;
+ int nr_sibling_threads;
+ char *sibling_threads;
+ int nr_numa_nodes;
+ char *numa_nodes;
+ int nr_pmu_mappings;
+ char *pmu_mappings;
+ int nr_groups;
+};
+
+struct perf_header {
+ enum perf_header_version version;
+ bool needs_swap;
+ u64 data_offset;
+ u64 data_size;
+ u64 feat_offset;
+ DECLARE_BITMAP(adds_features, HEADER_FEAT_BITS);
+ struct perf_session_env env;
+};
+
+struct perf_evlist;
+struct perf_session;
+
+int perf_session__read_header(struct perf_session *session);
+int perf_session__write_header(struct perf_session *session,
+ struct perf_evlist *evlist,
+ int fd, bool at_exit);
+int perf_header__write_pipe(int fd);
+
+void perf_header__set_feat(struct perf_header *header, int feat);
+void perf_header__clear_feat(struct perf_header *header, int feat);
+bool perf_header__has_feat(const struct perf_header *header, int feat);
+
+int perf_header__set_cmdline(int argc, const char **argv);
+
+int perf_header__process_sections(struct perf_header *header, int fd,
+ void *data,
+ int (*process)(struct perf_file_section *section,
+ struct perf_header *ph,
+ int feat, int fd, void *data));
+
+int perf_header__fprintf_info(struct perf_session *s, FILE *fp, bool full);
+
+int build_id_cache__add_s(const char *sbuild_id, const char *debugdir,
+ const char *name, bool is_kallsyms, bool is_vdso);
+int build_id_cache__remove_s(const char *sbuild_id, const char *debugdir);
+
+int perf_event__synthesize_attr(struct perf_tool *tool,
+ struct perf_event_attr *attr, u32 ids, u64 *id,
+ perf_event__handler_t process);
+int perf_event__synthesize_attrs(struct perf_tool *tool,
+ struct perf_session *session,
+ perf_event__handler_t process);
+int perf_event__process_attr(struct perf_tool *tool, union perf_event *event,
+ struct perf_evlist **pevlist);
+
+int perf_event__synthesize_tracing_data(struct perf_tool *tool,
+ int fd, struct perf_evlist *evlist,
+ perf_event__handler_t process);
+int perf_event__process_tracing_data(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_session *session);
+
+int perf_event__synthesize_build_id(struct perf_tool *tool,
+ struct dso *pos, u16 misc,
+ perf_event__handler_t process,
+ struct machine *machine);
+int perf_event__process_build_id(struct perf_tool *tool,
+ union perf_event *event,
+ struct perf_session *session);
+bool is_perf_magic(u64 magic);
+
+/*
+ * arch specific callback
+ */
+int get_cpuid(char *buffer, size_t sz);
+
+#endif /* __PERF_HEADER_H */
diff --git a/perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/bitops.h b/perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/bitops.h
new file mode 100644
index 00000000..45cf10a5
--- /dev/null
+++ b/perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/bitops.h
@@ -0,0 +1,158 @@
+#ifndef _PERF_LINUX_BITOPS_H_
+#define _PERF_LINUX_BITOPS_H_
+
+#include <linux/kernel.h>
+#include <linux/compiler.h>
+#include <asm/hweight.h>
+
+#ifndef __WORDSIZE
+#define __WORDSIZE (__SIZEOF_LONG__ * 8)
+#endif
+
+#define BITS_PER_LONG __WORDSIZE
+#define BITS_PER_BYTE 8
+#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(long))
+#define BITS_TO_U64(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u64))
+#define BITS_TO_U32(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE * sizeof(u32))
+#define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_BYTE)
+
+#define for_each_set_bit(bit, addr, size) \
+ for ((bit) = find_first_bit((addr), (size)); \
+ (bit) < (size); \
+ (bit) = find_next_bit((addr), (size), (bit) + 1))
+
+/* same as for_each_set_bit() but use bit as value to start with */
+#define for_each_set_bit_from(bit, addr, size) \
+ for ((bit) = find_next_bit((addr), (size), (bit)); \
+ (bit) < (size); \
+ (bit) = find_next_bit((addr), (size), (bit) + 1))
+
+static inline void set_bit(int nr, unsigned long *addr)
+{
+ addr[nr / BITS_PER_LONG] |= 1UL << (nr % BITS_PER_LONG);
+}
+
+static inline void clear_bit(int nr, unsigned long *addr)
+{
+ addr[nr / BITS_PER_LONG] &= ~(1UL << (nr % BITS_PER_LONG));
+}
+
+static __always_inline int test_bit(unsigned int nr, const unsigned long *addr)
+{
+ return ((1UL << (nr % BITS_PER_LONG)) &
+ (((unsigned long *)addr)[nr / BITS_PER_LONG])) != 0;
+}
+
+static inline unsigned long hweight_long(unsigned long w)
+{
+ return sizeof(w) == 4 ? hweight32(w) : hweight64(w);
+}
+
+#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG)
+
+/**
+ * __ffs - find first bit in word.
+ * @word: The word to search
+ *
+ * Undefined if no bit exists, so code should check against 0 first.
+ */
+static __always_inline unsigned long __ffs(unsigned long word)
+{
+ int num = 0;
+
+#if BITS_PER_LONG == 64
+ if ((word & 0xffffffff) == 0) {
+ num += 32;
+ word >>= 32;
+ }
+#endif
+ if ((word & 0xffff) == 0) {
+ num += 16;
+ word >>= 16;
+ }
+ if ((word & 0xff) == 0) {
+ num += 8;
+ word >>= 8;
+ }
+ if ((word & 0xf) == 0) {
+ num += 4;
+ word >>= 4;
+ }
+ if ((word & 0x3) == 0) {
+ num += 2;
+ word >>= 2;
+ }
+ if ((word & 0x1) == 0)
+ num += 1;
+ return num;
+}
+
+/*
+ * Find the first set bit in a memory region.
+ */
+static inline unsigned long
+find_first_bit(const unsigned long *addr, unsigned long size)
+{
+ const unsigned long *p = addr;
+ unsigned long result = 0;
+ unsigned long tmp;
+
+ while (size & ~(BITS_PER_LONG-1)) {
+ if ((tmp = *(p++)))
+ goto found;
+ result += BITS_PER_LONG;
+ size -= BITS_PER_LONG;
+ }
+ if (!size)
+ return result;
+
+ tmp = (*p) & (~0UL >> (BITS_PER_LONG - size));
+ if (tmp == 0UL) /* Are any bits set? */
+ return result + size; /* Nope. */
+found:
+ return result + __ffs(tmp);
+}
+
+/*
+ * Find the next set bit in a memory region.
+ */
+static inline unsigned long
+find_next_bit(const unsigned long *addr, unsigned long size, unsigned long offset)
+{
+ const unsigned long *p = addr + BITOP_WORD(offset);
+ unsigned long result = offset & ~(BITS_PER_LONG-1);
+ unsigned long tmp;
+
+ if (offset >= size)
+ return size;
+ size -= result;
+ offset %= BITS_PER_LONG;
+ if (offset) {
+ tmp = *(p++);
+ tmp &= (~0UL << offset);
+ if (size < BITS_PER_LONG)
+ goto found_first;
+ if (tmp)
+ goto found_middle;
+ size -= BITS_PER_LONG;
+ result += BITS_PER_LONG;
+ }
+ while (size & ~(BITS_PER_LONG-1)) {
+ if ((tmp = *(p++)))
+ goto found_middle;
+ result += BITS_PER_LONG;
+ size -= BITS_PER_LONG;
+ }
+ if (!size)
+ return result;
+ tmp = *p;
+
+found_first:
+ tmp &= (~0UL >> (BITS_PER_LONG - size));
+ if (tmp == 0UL) /* Are any bits set? */
+ return result + size; /* Nope. */
+found_middle:
+ return result + __ffs(tmp);
+}
+
+#endif
diff --git a/perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/kernel/kernel.h b/perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/kernel/kernel.h
new file mode 100644
index 00000000..d8c927c8
--- /dev/null
+++ b/perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/kernel/kernel.h
@@ -0,0 +1,134 @@
+#ifndef PERF_LINUX_KERNEL_H_
+#define PERF_LINUX_KERNEL_H_
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#define DIV_ROUND_UP(n,d) (((n) + (d) - 1) / (d))
+
+#define PERF_ALIGN(x, a) __PERF_ALIGN_MASK(x, (typeof(x))(a)-1)
+#define __PERF_ALIGN_MASK(x, mask) (((x)+(mask))&~(mask))
+
+#ifndef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#endif
+
+#ifndef container_of
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr: the pointer to the member.
+ * @type: the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#define container_of(ptr, type, member) ({ \
+ const typeof(((type *)0)->member) * __mptr = (ptr); \
+ (type *)((char *)__mptr - offsetof(type, member)); })
+#endif
+
+#define BUILD_BUG_ON_ZERO(e) (sizeof(struct { int:-!!(e); }))
+
+#ifndef max
+#define max(x, y) ({ \
+ typeof(x) _max1 = (x); \
+ typeof(y) _max2 = (y); \
+ (void) (&_max1 == &_max2); \
+ _max1 > _max2 ? _max1 : _max2; })
+#endif
+
+#ifndef min
+#define min(x, y) ({ \
+ typeof(x) _min1 = (x); \
+ typeof(y) _min2 = (y); \
+ (void) (&_min1 == &_min2); \
+ _min1 < _min2 ? _min1 : _min2; })
+#endif
+
+#ifndef roundup
+#define roundup(x, y) ( \
+{ \
+ const typeof(y) __y = y; \
+ (((x) + (__y - 1)) / __y) * __y; \
+} \
+)
+#endif
+
+#ifndef BUG_ON
+#ifdef NDEBUG
+#define BUG_ON(cond) do { if (cond) {} } while (0)
+#else
+#define BUG_ON(cond) assert(!(cond))
+#endif
+#endif
+
+/*
+ * Both need more care to handle endianness
+ * (Don't use bitmap_copy_le() for now)
+ */
+#define cpu_to_le64(x) (x)
+#define cpu_to_le32(x) (x)
+
+static inline int
+vscnprintf(char *buf, size_t size, const char *fmt, va_list args)
+{
+ int i;
+ ssize_t ssize = size;
+
+ i = vsnprintf(buf, size, fmt, args);
+
+ return (i >= ssize) ? (ssize - 1) : i;
+}
+
+static inline int scnprintf(char * buf, size_t size, const char * fmt, ...)
+{
+ va_list args;
+ ssize_t ssize = size;
+ int i;
+
+ va_start(args, fmt);
+ i = vsnprintf(buf, size, fmt, args);
+ va_end(args);
+
+ return (i >= ssize) ? (ssize - 1) : i;
+}
+
+static inline unsigned long
+simple_strtoul(const char *nptr, char **endptr, int base)
+{
+ return strtoul(nptr, endptr, base);
+}
+
+int eprintf(int level,
+ const char *fmt, ...) __attribute__((format(printf, 2, 3)));
+
+#ifndef pr_fmt
+#define pr_fmt(fmt) fmt
+#endif
+
+#define pr_err(fmt, ...) \
+ eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_warning(fmt, ...) \
+ eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_info(fmt, ...) \
+ eprintf(0, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debug(fmt, ...) \
+ eprintf(1, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debugN(n, fmt, ...) \
+ eprintf(n, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debug2(fmt, ...) pr_debugN(2, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debug3(fmt, ...) pr_debugN(3, pr_fmt(fmt), ##__VA_ARGS__)
+#define pr_debug4(fmt, ...) pr_debugN(4, pr_fmt(fmt), ##__VA_ARGS__)
+
+/*
+ * This looks more complex than it should be. But we need to
+ * get the type for the ~ right in round_down (it needs to be
+ * as wide as the result!), and we want to evaluate the macro
+ * arguments just once each.
+ */
+#define __round_mask(x, y) ((__typeof__(x))((y)-1))
+#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1)
+#define round_down(x, y) ((x) & ~__round_mask(x, y))
+
+#endif
diff --git a/perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/types.h b/perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/types.h
new file mode 100644
index 00000000..eb464786
--- /dev/null
+++ b/perfprofd/quipper/original-kernel-headers/tools/perf/util/include/linux/types.h
@@ -0,0 +1,29 @@
+#ifndef _PERF_LINUX_TYPES_H_
+#define _PERF_LINUX_TYPES_H_
+
+#include <asm/types.h>
+
+#ifndef __bitwise
+#define __bitwise
+#endif
+
+#ifndef __le32
+typedef __u32 __bitwise __le32;
+#endif
+
+#define DECLARE_BITMAP(name,bits) \
+ unsigned long name[BITS_TO_LONGS(bits)]
+
+struct list_head {
+ struct list_head *next, *prev;
+};
+
+struct hlist_head {
+ struct hlist_node *first;
+};
+
+struct hlist_node {
+ struct hlist_node *next, **pprev;
+};
+
+#endif
diff --git a/perfprofd/quipper/original-kernel-headers/tools/perf/util/target.h b/perfprofd/quipper/original-kernel-headers/tools/perf/util/target.h
new file mode 100644
index 00000000..a4be8575
--- /dev/null
+++ b/perfprofd/quipper/original-kernel-headers/tools/perf/util/target.h
@@ -0,0 +1,65 @@
+#ifndef _PERF_TARGET_H
+#define _PERF_TARGET_H
+
+#include <stdbool.h>
+#include <sys/types.h>
+
+struct perf_target {
+ const char *pid;
+ const char *tid;
+ const char *cpu_list;
+ const char *uid_str;
+ uid_t uid;
+ bool system_wide;
+ bool uses_mmap;
+};
+
+enum perf_target_errno {
+ PERF_ERRNO_TARGET__SUCCESS = 0,
+
+ /*
+ * Choose an arbitrary negative big number not to clash with standard
+ * errno since SUS requires the errno has distinct positive values.
+ * See 'Issue 6' in the link below.
+ *
+ * http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/errno.h.html
+ */
+ __PERF_ERRNO_TARGET__START = -10000,
+
+
+ /* for perf_target__validate() */
+ PERF_ERRNO_TARGET__PID_OVERRIDE_CPU = __PERF_ERRNO_TARGET__START,
+ PERF_ERRNO_TARGET__PID_OVERRIDE_UID,
+ PERF_ERRNO_TARGET__UID_OVERRIDE_CPU,
+ PERF_ERRNO_TARGET__PID_OVERRIDE_SYSTEM,
+ PERF_ERRNO_TARGET__UID_OVERRIDE_SYSTEM,
+
+ /* for perf_target__parse_uid() */
+ PERF_ERRNO_TARGET__INVALID_UID,
+ PERF_ERRNO_TARGET__USER_NOT_FOUND,
+
+ __PERF_ERRNO_TARGET__END,
+};
+
+enum perf_target_errno perf_target__validate(struct perf_target *target);
+enum perf_target_errno perf_target__parse_uid(struct perf_target *target);
+
+int perf_target__strerror(struct perf_target *target, int errnum, char *buf,
+ size_t buflen);
+
+static inline bool perf_target__has_task(struct perf_target *target)
+{
+ return target->tid || target->pid || target->uid_str;
+}
+
+static inline bool perf_target__has_cpu(struct perf_target *target)
+{
+ return target->system_wide || target->cpu_list;
+}
+
+static inline bool perf_target__none(struct perf_target *target)
+{
+ return !perf_target__has_task(target) && !perf_target__has_cpu(target);
+}
+
+#endif /* _PERF_TARGET_H */
diff --git a/perfprofd/quipper/original-kernel-headers/tools/perf/util/types.h b/perfprofd/quipper/original-kernel-headers/tools/perf/util/types.h
new file mode 100644
index 00000000..c51fa6b7
--- /dev/null
+++ b/perfprofd/quipper/original-kernel-headers/tools/perf/util/types.h
@@ -0,0 +1,24 @@
+#ifndef __PERF_TYPES_H
+#define __PERF_TYPES_H
+
+#include <stdint.h>
+
+/*
+ * We define u64 as uint64_t for every architecture
+ * so that we can print it with "%"PRIx64 without getting warnings.
+ */
+typedef uint64_t u64;
+typedef int64_t s64;
+typedef unsigned int u32;
+typedef signed int s32;
+typedef unsigned short u16;
+typedef signed short s16;
+typedef unsigned char u8;
+typedef signed char s8;
+
+union u64_swap {
+ u64 val64;
+ u32 val32[2];
+};
+
+#endif /* __PERF_TYPES_H */
diff --git a/perfprofd/quipper/perf_internals.h b/perfprofd/quipper/perf_internals.h
new file mode 100644
index 00000000..ef5a785d
--- /dev/null
+++ b/perfprofd/quipper/perf_internals.h
@@ -0,0 +1,64 @@
+/*
+**
+** Copyright 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 PERF_INTERNALS_H
+#define PERF_INTERNALS_H
+
+#include <linux/perf_event.h>
+#include "kernel-headers/tools/perf/util/types.h"
+#include "kernel-headers/tools/perf/util/include/linux/bitops.h"
+#include "kernel-headers/tools/perf/util/include/linux/types.h"
+#include "kernel-headers/tools/perf/util/build-id.h"
+#include "kernel-headers/tools/perf/util/include/linux/kernel/kernel.h"
+#include "kernel-headers/tools/perf/util/header.h"
+#include "kernel-headers/tools/perf/util/event.h"
+#include "kernel-headers/tools/perf/util/target.h"
+#include "kernel-headers/tools/perf/perf.h"
+
+// The first 64 bits of the perf header, used as a perf data file ID tag.
+const uint64_t kPerfMagic = 0x32454c4946524550LL; // "PERFILE2" little-endian
+
+#undef max
+#undef min
+
+//
+// Wrapper class to manage creation/deletion of storage associated
+// with perf_sample structs.
+//
+class PerfSampleCustodian {
+ public:
+ explicit PerfSampleCustodian(struct perf_sample& sample)
+ : sample_(sample) {
+ sample.raw_data = NULL;
+ sample.callchain = NULL;
+ sample.branch_stack = NULL;
+ }
+ ~PerfSampleCustodian() {
+ if (sample_.callchain)
+ delete [] sample_.callchain;
+ if (sample_.branch_stack)
+ delete [] sample_.branch_stack;
+ if (sample_.branch_stack)
+ delete [] reinterpret_cast<char*>(sample_.raw_data);
+ }
+ private:
+ struct perf_sample& sample_;
+};
+
+typedef perf_event event_t;
+
+#endif
diff --git a/perfprofd/quipper/perf_parser.cc b/perfprofd/quipper/perf_parser.cc
new file mode 100644
index 00000000..504b4f01
--- /dev/null
+++ b/perfprofd/quipper/perf_parser.cc
@@ -0,0 +1,576 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "perf_parser.h"
+
+#include <algorithm>
+#include <cstdio>
+#include <set>
+
+#include "base/logging.h"
+
+#include "address_mapper.h"
+#include "quipper_string.h"
+#include "perf_utils.h"
+
+namespace quipper {
+
+namespace {
+
+struct EventAndTime {
+ ParsedEvent* event;
+ uint64_t time;
+};
+
+// Returns true if |e1| has an earlier timestamp than |e2|. The args are const
+// pointers instead of references because of the way this function is used when
+// calling std::stable_sort.
+bool CompareParsedEventTimes(const std::unique_ptr<EventAndTime>& e1,
+ const std::unique_ptr<EventAndTime>& e2) {
+ return (e1->time < e2->time);
+}
+
+// Kernel MMAP entry pid appears as -1
+const uint32_t kKernelPid = UINT32_MAX;
+
+// Name and ID of the kernel swapper process.
+const char kSwapperCommandName[] = "swapper";
+const uint32_t kSwapperPid = 0;
+
+bool IsNullBranchStackEntry(const struct branch_entry& entry) {
+ return (!entry.from && !entry.to);
+}
+
+} // namespace
+
+PerfParser::PerfParser()
+ : kernel_mapper_(new AddressMapper)
+{}
+
+PerfParser::~PerfParser() {}
+
+PerfParser::PerfParser(const PerfParser::Options& options) {
+ options_ = options;
+}
+
+void PerfParser::set_options(const PerfParser::Options& options) {
+ options_ = options;
+}
+
+bool PerfParser::ParseRawEvents() {
+ process_mappers_.clear();
+ parsed_events_.resize(events_.size());
+ for (size_t i = 0; i < events_.size(); ++i) {
+ ParsedEvent& parsed_event = parsed_events_[i];
+ parsed_event.raw_event = events_[i].get();
+ }
+ MaybeSortParsedEvents();
+ if (!ProcessEvents()) {
+ return false;
+ }
+
+ if (!options_.discard_unused_events)
+ return true;
+
+ // Some MMAP/MMAP2 events' mapped regions will not have any samples. These
+ // MMAP/MMAP2 events should be dropped. |parsed_events_| should be
+ // reconstructed without these events.
+ size_t write_index = 0;
+ size_t read_index;
+ for (read_index = 0; read_index < parsed_events_.size(); ++read_index) {
+ const ParsedEvent& event = parsed_events_[read_index];
+ if ((event.raw_event->header.type == PERF_RECORD_MMAP ||
+ event.raw_event->header.type == PERF_RECORD_MMAP2) &&
+ event.num_samples_in_mmap_region == 0) {
+ continue;
+ }
+ if (read_index != write_index)
+ parsed_events_[write_index] = event;
+ ++write_index;
+ }
+ CHECK_LE(write_index, parsed_events_.size());
+ parsed_events_.resize(write_index);
+
+ // Now regenerate the sorted event list again. These are pointers to events
+ // so they must be regenerated after a resize() of the ParsedEvent vector.
+ MaybeSortParsedEvents();
+
+ return true;
+}
+
+void PerfParser::MaybeSortParsedEvents() {
+ if (!(sample_type_ & PERF_SAMPLE_TIME)) {
+ parsed_events_sorted_by_time_.resize(parsed_events_.size());
+ for (size_t i = 0; i < parsed_events_.size(); ++i) {
+ parsed_events_sorted_by_time_[i] = &parsed_events_[i];
+ }
+ return;
+ }
+ std::vector<std::unique_ptr<EventAndTime>> events_and_times;
+ events_and_times.resize(parsed_events_.size());
+ for (size_t i = 0; i < parsed_events_.size(); ++i) {
+ std::unique_ptr<EventAndTime> event_and_time(new EventAndTime);
+
+ // Store the timestamp and event pointer in an array.
+ event_and_time->event = &parsed_events_[i];
+
+ struct perf_sample sample_info;
+ PerfSampleCustodian custodian(sample_info);
+ CHECK(ReadPerfSampleInfo(*parsed_events_[i].raw_event, &sample_info));
+ event_and_time->time = sample_info.time;
+
+ events_and_times[i] = std::move(event_and_time);
+ }
+ // Sort the events based on timestamp, and then populate the sorted event
+ // vector in sorted order.
+ std::stable_sort(events_and_times.begin(), events_and_times.end(),
+ CompareParsedEventTimes);
+
+ parsed_events_sorted_by_time_.resize(events_and_times.size());
+ for (unsigned int i = 0; i < events_and_times.size(); ++i) {
+ parsed_events_sorted_by_time_[i] = events_and_times[i]->event;
+ }
+}
+
+bool PerfParser::ProcessEvents() {
+ memset(&stats_, 0, sizeof(stats_));
+
+ stats_.did_remap = false; // Explicitly clear the remap flag.
+
+ // Pid 0 is called the swapper process. Even though perf does not record a
+ // COMM event for pid 0, we act like we did receive a COMM event for it. Perf
+ // does this itself, example:
+ // http://lxr.free-electrons.com/source/tools/perf/util/session.c#L1120
+ commands_.insert(kSwapperCommandName);
+ pidtid_to_comm_map_[std::make_pair(kSwapperPid, kSwapperPid)] =
+ &(*commands_.find(kSwapperCommandName));
+
+ // NB: Not necessarily actually sorted by time.
+ for (unsigned int i = 0; i < parsed_events_sorted_by_time_.size(); ++i) {
+ ParsedEvent& parsed_event = *parsed_events_sorted_by_time_[i];
+ event_t& event = *parsed_event.raw_event;
+ switch (event.header.type) {
+ case PERF_RECORD_SAMPLE:
+ // SAMPLE doesn't have any fields to log at a fixed,
+ // previously-endian-swapped location. This used to log ip.
+ VLOG(1) << "SAMPLE";
+ ++stats_.num_sample_events;
+
+ if (MapSampleEvent(&parsed_event)) {
+ ++stats_.num_sample_events_mapped;
+ }
+ break;
+ case PERF_RECORD_MMAP: {
+ VLOG(1) << "MMAP: " << event.mmap.filename;
+ ++stats_.num_mmap_events;
+ // Use the array index of the current mmap event as a unique identifier.
+ CHECK(MapMmapEvent(&event.mmap, i)) << "Unable to map MMAP event!";
+ // No samples in this MMAP region yet, hopefully.
+ parsed_event.num_samples_in_mmap_region = 0;
+ DSOInfo dso_info;
+ // TODO(sque): Add Build ID as well.
+ dso_info.name = event.mmap.filename;
+ dso_set_.insert(dso_info);
+ break;
+ }
+ case PERF_RECORD_MMAP2: {
+ VLOG(1) << "MMAP2: " << event.mmap2.filename;
+ ++stats_.num_mmap_events;
+ // Use the array index of the current mmap event as a unique identifier.
+ CHECK(MapMmapEvent(&event.mmap2, i)) << "Unable to map MMAP2 event!";
+ // No samples in this MMAP region yet, hopefully.
+ parsed_event.num_samples_in_mmap_region = 0;
+ DSOInfo dso_info;
+ // TODO(sque): Add Build ID as well.
+ dso_info.name = event.mmap2.filename;
+ dso_set_.insert(dso_info);
+ break;
+ }
+ case PERF_RECORD_FORK:
+ VLOG(1) << "FORK: " << event.fork.ppid << ":" << event.fork.ptid
+ << " -> " << event.fork.pid << ":" << event.fork.tid;
+ ++stats_.num_fork_events;
+ CHECK(MapForkEvent(event.fork)) << "Unable to map FORK event!";
+ break;
+ case PERF_RECORD_EXIT:
+ // EXIT events have the same structure as FORK events.
+ VLOG(1) << "EXIT: " << event.fork.ppid << ":" << event.fork.ptid;
+ ++stats_.num_exit_events;
+ break;
+ case PERF_RECORD_COMM:
+ VLOG(1) << "COMM: " << event.comm.pid << ":" << event.comm.tid << ": "
+ << event.comm.comm;
+ ++stats_.num_comm_events;
+ CHECK(MapCommEvent(event.comm));
+ commands_.insert(event.comm.comm);
+ pidtid_to_comm_map_[std::make_pair(event.comm.pid, event.comm.tid)] =
+ &(*commands_.find(event.comm.comm));
+ break;
+ case PERF_RECORD_LOST:
+ case PERF_RECORD_THROTTLE:
+ case PERF_RECORD_UNTHROTTLE:
+ case PERF_RECORD_READ:
+ case PERF_RECORD_MAX:
+ VLOG(1) << "Parsed event type: " << event.header.type
+ << ". Doing nothing.";
+ break;
+ default:
+ LOG(ERROR) << "Unknown event type: " << event.header.type;
+ return false;
+ }
+ }
+
+ // Print stats collected from parsing.
+ DLOG(INFO) << "Parser processed: "
+ << stats_.num_mmap_events << " MMAP/MMAP2 events, "
+ << stats_.num_comm_events << " COMM events, "
+ << stats_.num_fork_events << " FORK events, "
+ << stats_.num_exit_events << " EXIT events, "
+ << stats_.num_sample_events << " SAMPLE events, "
+ << stats_.num_sample_events_mapped << " of these were mapped";
+
+ float sample_mapping_percentage =
+ static_cast<float>(stats_.num_sample_events_mapped) /
+ stats_.num_sample_events * 100.;
+ float threshold = options_.sample_mapping_percentage_threshold;
+ if (sample_mapping_percentage < threshold) {
+ LOG(WARNING) << "Mapped " << static_cast<int>(sample_mapping_percentage)
+ << "% of samples, expected at least "
+ << static_cast<int>(threshold) << "%";
+ return false;
+ }
+ stats_.did_remap = options_.do_remap;
+ return true;
+}
+
+bool PerfParser::MapSampleEvent(ParsedEvent* parsed_event) {
+ bool mapping_failed = false;
+
+ // Find the associated command.
+ if (!(sample_type_ & PERF_SAMPLE_IP && sample_type_ & PERF_SAMPLE_TID))
+ return false;
+ perf_sample sample_info;
+ PerfSampleCustodian custodian(sample_info);
+ if (!ReadPerfSampleInfo(*parsed_event->raw_event, &sample_info))
+ return false;
+ PidTid pidtid = std::make_pair(sample_info.pid, sample_info.tid);
+ const auto comm_iter = pidtid_to_comm_map_.find(pidtid);
+ if (comm_iter != pidtid_to_comm_map_.end()) {
+ parsed_event->set_command(comm_iter->second);
+ }
+
+ const uint64_t unmapped_event_ip = sample_info.ip;
+
+ // Map the event IP itself.
+ if (!MapIPAndPidAndGetNameAndOffset(sample_info.ip,
+ sample_info.pid,
+ &sample_info.ip,
+ &parsed_event->dso_and_offset)) {
+ mapping_failed = true;
+ }
+
+ if (sample_info.callchain &&
+ !MapCallchain(sample_info.ip,
+ sample_info.pid,
+ unmapped_event_ip,
+ sample_info.callchain,
+ parsed_event)) {
+ mapping_failed = true;
+ }
+
+ if (sample_info.branch_stack &&
+ !MapBranchStack(sample_info.pid,
+ sample_info.branch_stack,
+ parsed_event)) {
+ mapping_failed = true;
+ }
+
+ // Write the remapped data back to the raw event regardless of whether it was
+ // entirely successfully remapped. A single failed remap should not
+ // invalidate all the other remapped entries.
+ if (!WritePerfSampleInfo(sample_info, parsed_event->raw_event)) {
+ LOG(ERROR) << "Failed to write back remapped sample info.";
+ return false;
+ }
+
+ return !mapping_failed;
+}
+
+bool PerfParser::MapCallchain(const uint64_t ip,
+ const uint32_t pid,
+ const uint64_t original_event_addr,
+ struct ip_callchain* callchain,
+ ParsedEvent* parsed_event) {
+ if (!callchain) {
+ LOG(ERROR) << "NULL call stack data.";
+ return false;
+ }
+
+ bool mapping_failed = false;
+
+ // If the callchain's length is 0, there is no work to do.
+ if (callchain->nr == 0)
+ return true;
+
+ // Keeps track of whether the current entry is kernel or user.
+ parsed_event->callchain.resize(callchain->nr);
+ int num_entries_mapped = 0;
+ for (unsigned int j = 0; j < callchain->nr; ++j) {
+ uint64_t entry = callchain->ips[j];
+ // When a callchain context entry is found, do not attempt to symbolize it.
+ if (entry >= PERF_CONTEXT_MAX) {
+ continue;
+ }
+ // The sample address has already been mapped so no need to map it.
+ if (entry == original_event_addr) {
+ callchain->ips[j] = ip;
+ continue;
+ }
+ if (!MapIPAndPidAndGetNameAndOffset(
+ entry,
+ pid,
+ &callchain->ips[j],
+ &parsed_event->callchain[num_entries_mapped++])) {
+ mapping_failed = true;
+ }
+ }
+ // Not all the entries were mapped. Trim |parsed_event->callchain| to
+ // remove unused entries at the end.
+ parsed_event->callchain.resize(num_entries_mapped);
+
+ return !mapping_failed;
+}
+
+bool PerfParser::MapBranchStack(const uint32_t pid,
+ struct branch_stack* branch_stack,
+ ParsedEvent* parsed_event) {
+ if (!branch_stack) {
+ LOG(ERROR) << "NULL branch stack data.";
+ return false;
+ }
+
+ // First, trim the branch stack to remove trailing null entries.
+ size_t trimmed_size = 0;
+ for (size_t i = 0; i < branch_stack->nr; ++i) {
+ // Count the number of non-null entries before the first null entry.
+ if (IsNullBranchStackEntry(branch_stack->entries[i])) {
+ break;
+ }
+ ++trimmed_size;
+ }
+
+ // If a null entry was found, make sure all subsequent null entries are NULL
+ // as well.
+ for (size_t i = trimmed_size; i < branch_stack->nr; ++i) {
+ const struct branch_entry& entry = branch_stack->entries[i];
+ if (!IsNullBranchStackEntry(entry)) {
+ LOG(ERROR) << "Non-null branch stack entry found after null entry: "
+ << reinterpret_cast<void*>(entry.from) << " -> "
+ << reinterpret_cast<void*>(entry.to);
+ return false;
+ }
+ }
+
+ // Map branch stack addresses.
+ parsed_event->branch_stack.resize(trimmed_size);
+ for (unsigned int i = 0; i < trimmed_size; ++i) {
+ struct branch_entry& entry = branch_stack->entries[i];
+ ParsedEvent::BranchEntry& parsed_entry = parsed_event->branch_stack[i];
+ if (!MapIPAndPidAndGetNameAndOffset(entry.from,
+ pid,
+ &entry.from,
+ &parsed_entry.from)) {
+ return false;
+ }
+ if (!MapIPAndPidAndGetNameAndOffset(entry.to,
+ pid,
+ &entry.to,
+ &parsed_entry.to)) {
+ return false;
+ }
+ parsed_entry.predicted = entry.flags.predicted;
+ // Either predicted or mispredicted, not both. But don't use a CHECK here,
+ // just exit gracefully because it's a minor issue.
+ if (entry.flags.predicted == entry.flags.mispred) {
+ LOG(ERROR) << "Branch stack entry predicted and mispred flags "
+ << "both have value " << entry.flags.mispred;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool PerfParser::MapIPAndPidAndGetNameAndOffset(
+ uint64_t ip,
+ uint32_t pid,
+ uint64_t* new_ip,
+ ParsedEvent::DSOAndOffset* dso_and_offset) {
+
+ // Attempt to find the synthetic address of the IP sample in this order:
+ // 1. Address space of its own process.
+ // 2. Address space of the kernel.
+
+ uint64_t mapped_addr = 0;
+
+ // Sometimes the first event we see is a SAMPLE event and we don't have the
+ // time to create an address mapper for a process. Example, for pid 0.
+ AddressMapper* mapper = GetOrCreateProcessMapper(pid).first;
+ bool mapped = mapper->GetMappedAddress(ip, &mapped_addr);
+ if (!mapped) {
+ mapper = kernel_mapper_.get();
+ mapped = mapper->GetMappedAddress(ip, &mapped_addr);
+ }
+
+ // TODO(asharif): What should we do when we cannot map a SAMPLE event?
+ if (mapped) {
+ if (dso_and_offset) {
+ uint64_t id = kuint64max;
+ CHECK(mapper->GetMappedIDAndOffset(ip, &id, &dso_and_offset->offset_));
+ // Make sure the ID points to a valid event.
+ CHECK_LE(id, parsed_events_sorted_by_time_.size());
+ ParsedEvent* parsed_event = parsed_events_sorted_by_time_[id];
+ const event_t* raw_event = parsed_event->raw_event;
+
+ DSOInfo dso_info;
+ if (raw_event->header.type == PERF_RECORD_MMAP) {
+ dso_info.name = raw_event->mmap.filename;
+ } else if (raw_event->header.type == PERF_RECORD_MMAP2) {
+ dso_info.name = raw_event->mmap2.filename;
+ } else {
+ LOG(FATAL) << "Expected MMAP or MMAP2 event";
+ }
+
+ // Find the mmap DSO filename in the set of known DSO names.
+ // TODO(sque): take build IDs into account.
+ std::set<DSOInfo>::const_iterator dso_iter = dso_set_.find(dso_info);
+ CHECK(dso_iter != dso_set_.end());
+ dso_and_offset->dso_info_ = &(*dso_iter);
+
+ ++parsed_event->num_samples_in_mmap_region;
+ }
+ if (options_.do_remap)
+ *new_ip = mapped_addr;
+ }
+ return mapped;
+}
+
+bool PerfParser::MapMmapEvent(uint64_t id,
+ uint32_t pid,
+ uint64_t* p_start,
+ uint64_t* p_len,
+ uint64_t* p_pgoff)
+{
+ // We need to hide only the real kernel addresses. However, to make things
+ // more secure, and make the mapping idempotent, we should remap all
+ // addresses, both kernel and non-kernel.
+ AddressMapper* mapper =
+ (pid == kKernelPid ? kernel_mapper_.get() :
+ GetOrCreateProcessMapper(pid).first);
+
+ uint64_t start = *p_start;
+ uint64_t len = *p_len;
+ uint64_t pgoff = *p_pgoff;
+
+ // |id| == 0 corresponds to the kernel mmap. We have several cases here:
+ //
+ // For ARM and x86, in sudo mode, pgoff == start, example:
+ // start=0x80008200
+ // pgoff=0x80008200
+ // len =0xfffffff7ff7dff
+ //
+ // For x86-64, in sudo mode, pgoff is between start and start + len. SAMPLE
+ // events lie between pgoff and pgoff + length of the real kernel binary,
+ // example:
+ // start=0x3bc00000
+ // pgoff=0xffffffffbcc00198
+ // len =0xffffffff843fffff
+ // SAMPLE events will be found after pgoff. For kernels with ASLR, pgoff will
+ // be something only visible to the root user, and will be randomized at
+ // startup. With |remap| set to true, we should hide pgoff in this case. So we
+ // normalize all SAMPLE events relative to pgoff.
+ //
+ // For non-sudo mode, the kernel will be mapped from 0 to the pointer limit,
+ // example:
+ // start=0x0
+ // pgoff=0x0
+ // len =0xffffffff
+ if (id == 0) {
+ // If pgoff is between start and len, we normalize the event by setting
+ // start to be pgoff just like how it is for ARM and x86. We also set len to
+ // be a much smaller number (closer to the real length of the kernel binary)
+ // because SAMPLEs are actually only seen between |event->pgoff| and
+ // |event->pgoff + kernel text size|.
+ if (pgoff > start && pgoff < start + len) {
+ len = len + start - pgoff;
+ start = pgoff;
+ }
+ // For kernels with ALSR pgoff is critical information that should not be
+ // revealed when |remap| is true.
+ pgoff = 0;
+ }
+
+ if (!mapper->MapWithID(start, len, id, pgoff, true)) {
+ mapper->DumpToLog();
+ return false;
+ }
+
+ if (options_.do_remap) {
+ uint64_t mapped_addr;
+ CHECK(mapper->GetMappedAddress(start, &mapped_addr));
+ *p_start = mapped_addr;
+ *p_len = len;
+ *p_pgoff = pgoff;
+ }
+ return true;
+}
+
+std::pair<AddressMapper*, bool> PerfParser::GetOrCreateProcessMapper(
+ uint32_t pid, uint32_t *ppid) {
+ const auto& search = process_mappers_.find(pid);
+ if (search != process_mappers_.end()) {
+ return std::make_pair(search->second.get(), false);
+ }
+
+ std::unique_ptr<AddressMapper> mapper;
+ const auto& parent_mapper = (ppid ? process_mappers_.find(*ppid) : process_mappers_.end());
+ if (parent_mapper != process_mappers_.end())
+ mapper.reset(new AddressMapper(*parent_mapper->second));
+ else
+ mapper.reset(new AddressMapper());
+
+ const auto inserted =
+ process_mappers_.insert(search, std::make_pair(pid, std::move(mapper)));
+ return std::make_pair(inserted->second.get(), true);
+}
+
+bool PerfParser::MapCommEvent(const struct comm_event& event) {
+ GetOrCreateProcessMapper(event.pid);
+ return true;
+}
+
+bool PerfParser::MapForkEvent(const struct fork_event& event) {
+ PidTid parent = std::make_pair(event.ppid, event.ptid);
+ PidTid child = std::make_pair(event.pid, event.tid);
+ if (parent != child &&
+ pidtid_to_comm_map_.find(parent) != pidtid_to_comm_map_.end()) {
+ pidtid_to_comm_map_[child] = pidtid_to_comm_map_[parent];
+ }
+
+ const uint32_t pid = event.pid;
+
+ // If the parent and child pids are the same, this is just a new thread
+ // within the same process, so don't do anything.
+ if (event.ppid == pid)
+ return true;
+
+ uint32_t ppid = event.ppid;
+ if (!GetOrCreateProcessMapper(pid, &ppid).second) {
+ DLOG(INFO) << "Found an existing process mapper with pid: " << pid;
+ }
+
+ return true;
+}
+
+} // namespace quipper
diff --git a/perfprofd/quipper/perf_parser.h b/perfprofd/quipper/perf_parser.h
new file mode 100644
index 00000000..bb66de20
--- /dev/null
+++ b/perfprofd/quipper/perf_parser.h
@@ -0,0 +1,249 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMIUMOS_WIDE_PROFILING_PERF_PARSER_H_
+#define CHROMIUMOS_WIDE_PROFILING_PERF_PARSER_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <set>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/macros.h"
+
+#include "perf_reader.h"
+#include "perf_utils.h"
+
+namespace quipper {
+
+class AddressMapper;
+
+// A struct containing all relevant info for a mapped DSO, independent of any
+// samples.
+struct DSOInfo {
+ string name;
+ string build_id;
+
+ // Comparator that allows this to be stored in a STL set.
+ bool operator<(const DSOInfo& other) const {
+ if (name == other.name)
+ return build_id < other.build_id;
+ return name < other.name;
+ }
+};
+
+struct ParsedEvent {
+ // TODO(sque): Turn this struct into a class to privatize member variables.
+ ParsedEvent() : command_(NULL) {}
+
+ // Stores address of an event_t owned by the |PerfReader::events_| vector.
+ event_t* raw_event;
+
+ // For mmap events, use this to count the number of samples that are in this
+ // region.
+ uint32_t num_samples_in_mmap_region;
+
+ // Command associated with this sample.
+ const string* command_;
+
+ // Accessor for command string.
+ const string command() const {
+ if (command_)
+ return *command_;
+ return string();
+ }
+
+ void set_command(const string* command) {
+ command_ = command;
+ }
+
+ // A struct that contains a DSO + offset pair.
+ struct DSOAndOffset {
+ const DSOInfo* dso_info_;
+ uint64_t offset_;
+
+ // Accessor methods.
+ const string dso_name() const {
+ if (dso_info_)
+ return dso_info_->name;
+ return string();
+ }
+ const string build_id() const {
+ if (dso_info_)
+ return dso_info_->build_id;
+ return string();
+ }
+ uint64_t offset() const {
+ return offset_;
+ }
+
+ DSOAndOffset() : dso_info_(NULL),
+ offset_(0) {}
+ } dso_and_offset;
+
+ // DSO+offset info for callchain.
+ std::vector<DSOAndOffset> callchain;
+
+ // DSO + offset info for branch stack entries.
+ struct BranchEntry {
+ bool predicted;
+ DSOAndOffset from;
+ DSOAndOffset to;
+ };
+ std::vector<BranchEntry> branch_stack;
+};
+
+struct PerfEventStats {
+ // Number of each type of event.
+ uint32_t num_sample_events;
+ uint32_t num_mmap_events;
+ uint32_t num_comm_events;
+ uint32_t num_fork_events;
+ uint32_t num_exit_events;
+
+ // Number of sample events that were successfully mapped using the address
+ // mapper. The mapping is recorded regardless of whether the address in the
+ // perf sample event itself was assigned the remapped address. The latter is
+ // indicated by |did_remap|.
+ uint32_t num_sample_events_mapped;
+
+ // Whether address remapping was enabled during event parsing.
+ bool did_remap;
+};
+
+class PerfParser : public PerfReader {
+ public:
+ PerfParser();
+ ~PerfParser();
+
+ struct Options {
+ // For synthetic address mapping.
+ bool do_remap = false;
+ // Set this flag to discard non-sample events that don't have any associated
+ // sample events. e.g. MMAP regions with no samples in them.
+ bool discard_unused_events = false;
+ // When mapping perf sample events, at least this percentage of them must be
+ // successfully mapped in order for ProcessEvents() to return true.
+ // By default, most samples must be properly mapped in order for sample
+ // mapping to be considered successful.
+ float sample_mapping_percentage_threshold = 95.0f;
+ };
+
+ // Constructor that takes in options at PerfParser creation time.
+ explicit PerfParser(const Options& options);
+
+ // Pass in a struct containing various options.
+ void set_options(const Options& options);
+
+ // Gets parsed event/sample info from raw event data.
+ bool ParseRawEvents();
+
+ const std::vector<ParsedEvent>& parsed_events() const {
+ return parsed_events_;
+ }
+
+ // Returns an array of pointers to |parsed_events_| sorted by sample time.
+ // The first time this is called, it will create the sorted array.
+ const std::vector<ParsedEvent*>& GetEventsSortedByTime() const {
+ return parsed_events_sorted_by_time_;
+ }
+
+ const PerfEventStats& stats() const {
+ return stats_;
+ }
+
+ protected:
+ // Defines a type for a pid:tid pair.
+ typedef std::pair<uint32_t, uint32_t> PidTid;
+
+ // Sort |parsed_events_| by time, storing the results in
+ // |parsed_events_sorted_by_time_|.
+ // Events can not be sorted by time if PERF_SAMPLE_TIME is not set in
+ // attr.sample_type (PerfReader.sample_type_). In that case,
+ // |parsed_events_sorted_by_time_| is not actually sorted, but has the same
+ // order as |parsed_events_|.
+ void MaybeSortParsedEvents();
+
+ // Used for processing events. e.g. remapping with synthetic addresses.
+ bool ProcessEvents();
+ template <typename MMapEventT>
+ bool MapMmapEvent(MMapEventT* event, uint64_t id) {
+ return MapMmapEvent(id,
+ event->pid,
+ &event->start,
+ &event->len,
+ &event->pgoff);
+ }
+ bool MapMmapEvent(uint64_t id,
+ uint32_t pid,
+ uint64_t* p_start,
+ uint64_t* p_len,
+ uint64_t* p_pgoff);
+ bool MapForkEvent(const struct fork_event& event);
+ bool MapCommEvent(const struct comm_event& event);
+
+ // Does a sample event remap and then returns DSO name and offset of sample.
+ bool MapSampleEvent(ParsedEvent* parsed_event);
+
+ std::vector<ParsedEvent> parsed_events_;
+ // See MaybeSortParsedEvents to see why this might not actually be sorted
+ // by time:
+ std::vector<ParsedEvent*> parsed_events_sorted_by_time_;
+
+ Options options_; // Store all option flags as one struct.
+
+ // Maps pid/tid to commands.
+ std::map<PidTid, const string*> pidtid_to_comm_map_;
+
+ // A set to store the actual command strings.
+ std::set<string> commands_;
+
+ PerfEventStats stats_;
+
+ // A set of unique DSOs that may be referenced by multiple events.
+ std::set<DSOInfo> dso_set_;
+
+ private:
+ // Calls MapIPAndPidAndGetNameAndOffset() on the callchain of a sample event.
+ bool MapCallchain(const uint64_t ip,
+ const uint32_t pid,
+ uint64_t original_event_addr,
+ struct ip_callchain* callchain,
+ ParsedEvent* parsed_event);
+
+ // Trims the branch stack for null entries and calls
+ // MapIPAndPidAndGetNameAndOffset() on each entry.
+ bool MapBranchStack(const uint32_t pid,
+ struct branch_stack* branch_stack,
+ ParsedEvent* parsed_event);
+
+ // This maps a sample event and returns the mapped address, DSO name, and
+ // offset within the DSO. This is a private function because the API might
+ // change in the future, and we don't want derived classes to be stuck with an
+ // obsolete API.
+ bool MapIPAndPidAndGetNameAndOffset(
+ uint64_t ip,
+ uint32_t pid,
+ uint64_t* new_ip,
+ ParsedEvent::DSOAndOffset* dso_and_offset);
+
+ // Create a process mapper for a process. Optionally pass in a parent pid
+ // |ppid| from which to copy mappings.
+ // Returns (mapper, true) if a new AddressMapper was created, and
+ // (mapper, false) if there is an existing mapper.
+ std::pair<AddressMapper*, bool> GetOrCreateProcessMapper(uint32_t pid,
+ uint32_t *ppid = NULL);
+
+ std::unique_ptr<AddressMapper> kernel_mapper_;
+ std::map<uint32_t, std::unique_ptr<AddressMapper>> process_mappers_;
+
+ DISALLOW_COPY_AND_ASSIGN(PerfParser);
+};
+
+} // namespace quipper
+
+#endif // CHROMIUMOS_WIDE_PROFILING_PERF_PARSER_H_
diff --git a/perfprofd/quipper/perf_reader.cc b/perfprofd/quipper/perf_reader.cc
new file mode 100644
index 00000000..99731d45
--- /dev/null
+++ b/perfprofd/quipper/perf_reader.cc
@@ -0,0 +1,1645 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "perf_reader.h"
+
+#include <byteswap.h>
+#include <limits.h>
+
+#include <bitset>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <vector>
+
+#define LOG_TAG "perf_reader"
+
+#include "base/logging.h"
+
+#include "quipper_string.h"
+#include "perf_utils.h"
+
+namespace quipper {
+
+struct BufferWithSize {
+ char* ptr;
+ size_t size;
+};
+
+// If the buffer is read-only, it is not sufficient to mark the previous struct
+// as const, as this only means that the pointer cannot be changed, and says
+// nothing about the contents of the buffer. So, we need another struct.
+struct ConstBufferWithSize {
+ const char* ptr;
+ size_t size;
+};
+
+namespace {
+
+// The type of the number of string data, found in the command line metadata in
+// the perf data file.
+typedef u32 num_string_data_type;
+
+// Types of the event desc fields that are not found in other structs.
+typedef u32 event_desc_num_events;
+typedef u32 event_desc_attr_size;
+typedef u32 event_desc_num_unique_ids;
+
+// The type of the number of nodes field in NUMA topology.
+typedef u32 numa_topology_num_nodes_type;
+
+// A mask that is applied to metadata_mask_ in order to get a mask for
+// only the metadata supported by quipper.
+const uint32_t kSupportedMetadataMask =
+ 1 << HEADER_TRACING_DATA |
+ 1 << HEADER_BUILD_ID |
+ 1 << HEADER_HOSTNAME |
+ 1 << HEADER_OSRELEASE |
+ 1 << HEADER_VERSION |
+ 1 << HEADER_ARCH |
+ 1 << HEADER_NRCPUS |
+ 1 << HEADER_CPUDESC |
+ 1 << HEADER_CPUID |
+ 1 << HEADER_TOTAL_MEM |
+ 1 << HEADER_CMDLINE |
+ 1 << HEADER_EVENT_DESC |
+ 1 << HEADER_CPU_TOPOLOGY |
+ 1 << HEADER_NUMA_TOPOLOGY |
+ 1 << HEADER_BRANCH_STACK;
+
+// By default, the build ID event has PID = -1.
+const uint32_t kDefaultBuildIDEventPid = static_cast<uint32_t>(-1);
+
+template <class T>
+void ByteSwap(T* input) {
+ switch (sizeof(T)) {
+ case sizeof(uint8_t):
+ LOG(WARNING) << "Attempting to byte swap on a single byte.";
+ break;
+ case sizeof(uint16_t):
+ *input = bswap_16(*input);
+ break;
+ case sizeof(uint32_t):
+ *input = bswap_32(*input);
+ break;
+ case sizeof(uint64_t):
+ *input = bswap_64(*input);
+ break;
+ default:
+ LOG(FATAL) << "Invalid size for byte swap: " << sizeof(T) << " bytes";
+ break;
+ }
+}
+
+u64 MaybeSwap(u64 value, bool swap) {
+ if (swap)
+ return bswap_64(value);
+ return value;
+}
+
+u32 MaybeSwap(u32 value, bool swap) {
+ if (swap)
+ return bswap_32(value);
+ return value;
+}
+
+u8 ReverseByte(u8 x) {
+ x = (x & 0xf0) >> 4 | (x & 0x0f) << 4; // exchange nibbles
+ x = (x & 0xcc) >> 2 | (x & 0x33) << 2; // exchange pairs
+ x = (x & 0xaa) >> 1 | (x & 0x55) << 1; // exchange neighbors
+ return x;
+}
+
+// If field points to the start of a bitfield padded to len bytes, this
+// performs an endian swap of the bitfield, assuming the compiler that produced
+// it conforms to the same ABI (bitfield layout is not completely specified by
+// the language).
+void SwapBitfieldOfBits(u8* field, size_t len) {
+ for (size_t i = 0; i < len; i++) {
+ field[i] = ReverseByte(field[i]);
+ }
+}
+
+// The code currently assumes that the compiler will not add any padding to the
+// various structs. These CHECKs make sure that this is true.
+void CheckNoEventHeaderPadding() {
+ perf_event_header header;
+ CHECK_EQ(sizeof(header),
+ sizeof(header.type) + sizeof(header.misc) + sizeof(header.size));
+}
+
+void CheckNoPerfEventAttrPadding() {
+ perf_event_attr attr;
+ CHECK_EQ(sizeof(attr),
+ (reinterpret_cast<u64>(&attr.__reserved_2) -
+ reinterpret_cast<u64>(&attr)) +
+ sizeof(attr.__reserved_2));
+}
+
+void CheckNoEventTypePadding() {
+ perf_trace_event_type event_type;
+ CHECK_EQ(sizeof(event_type),
+ sizeof(event_type.event_id) + sizeof(event_type.name));
+}
+
+void CheckNoBuildIDEventPadding() {
+ build_id_event event;
+ CHECK_EQ(sizeof(event),
+ sizeof(event.header.type) + sizeof(event.header.misc) +
+ sizeof(event.header.size) + sizeof(event.pid) +
+ sizeof(event.build_id));
+}
+
+// Creates/updates a build id event with |build_id| and |filename|.
+// Passing "" to |build_id| or |filename| will leave the corresponding field
+// unchanged (in which case |event| must be non-null).
+// If |event| is null or is not large enough, a new event will be created.
+// In this case, if |event| is non-null, it will be freed.
+// Otherwise, updates the fields of the existing event.
+// |new_misc| indicates kernel vs user space, and is only used to fill in the
+// |header.misc| field of new events.
+// In either case, returns a pointer to the event containing the updated data,
+// or NULL in the case of a failure.
+build_id_event* CreateOrUpdateBuildID(const string& build_id,
+ const string& filename,
+ uint16_t new_misc,
+ build_id_event* event) {
+ // When creating an event from scratch, build id and filename must be present.
+ if (!event && (build_id.empty() || filename.empty()))
+ return NULL;
+ size_t new_len = GetUint64AlignedStringLength(
+ filename.empty() ? event->filename : filename);
+
+ // If event is null, or we don't have enough memory, allocate more memory, and
+ // switch the new pointer with the existing pointer.
+ size_t new_size = sizeof(*event) + new_len;
+ if (!event || new_size > event->header.size) {
+ build_id_event* new_event = CallocMemoryForBuildID(new_size);
+
+ if (event) {
+ // Copy over everything except the filename and free the event.
+ // It is guaranteed that we are changing the filename - otherwise, the old
+ // size and the new size would be equal.
+ *new_event = *event;
+ free(event);
+ } else {
+ // Fill in the fields appropriately.
+ new_event->header.type = HEADER_BUILD_ID;
+ new_event->header.misc = new_misc;
+ new_event->pid = kDefaultBuildIDEventPid;
+ }
+ event = new_event;
+ }
+
+ // Here, event is the pointer to the build_id_event that we are keeping.
+ // Update the event's size, build id, and filename.
+ if (!build_id.empty() &&
+ !StringToHex(build_id, event->build_id, arraysize(event->build_id))) {
+ free(event);
+ return NULL;
+ }
+
+ if (!filename.empty())
+ CHECK_GT(snprintf(event->filename, new_len, "%s", filename.c_str()), 0);
+
+ event->header.size = new_size;
+ return event;
+}
+
+// Reads |size| bytes from |buffer| into |dest| and advances |src_offset|.
+bool ReadDataFromBuffer(const ConstBufferWithSize& buffer,
+ size_t size,
+ const string& value_name,
+ size_t* src_offset,
+ void* dest) {
+ size_t end_offset = *src_offset + size / sizeof(*buffer.ptr);
+ if (buffer.size < end_offset) {
+ LOG(ERROR) << "Not enough bytes to read " << value_name
+ << ". Requested " << size << " bytes";
+ return false;
+ }
+ memcpy(dest, buffer.ptr + *src_offset, size);
+ *src_offset = end_offset;
+ return true;
+}
+
+// Reads a CStringWithLength from |buffer| into |dest|, and advances the offset.
+bool ReadStringFromBuffer(const ConstBufferWithSize& buffer,
+ bool is_cross_endian,
+ size_t* offset,
+ CStringWithLength* dest) {
+ if (!ReadDataFromBuffer(buffer, sizeof(dest->len), "string length",
+ offset, &dest->len)) {
+ return false;
+ }
+ if (is_cross_endian)
+ ByteSwap(&dest->len);
+
+ if (buffer.size < *offset + dest->len) {
+ LOG(ERROR) << "Not enough bytes to read string";
+ return false;
+ }
+ dest->str = string(buffer.ptr + *offset);
+ *offset += dest->len / sizeof(*buffer.ptr);
+ return true;
+}
+
+// Read read info from perf data. Corresponds to sample format type
+// PERF_SAMPLE_READ.
+const uint64_t* ReadReadInfo(const uint64_t* array,
+ bool swap_bytes,
+ uint64_t read_format,
+ struct perf_sample* sample) {
+ if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
+ sample->read.time_enabled = *array++;
+ if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
+ sample->read.time_running = *array++;
+ if (read_format & PERF_FORMAT_ID)
+ sample->read.one.id = *array++;
+
+ if (swap_bytes) {
+ ByteSwap(&sample->read.time_enabled);
+ ByteSwap(&sample->read.time_running);
+ ByteSwap(&sample->read.one.id);
+ }
+
+ return array;
+}
+
+// Read call chain info from perf data. Corresponds to sample format type
+// PERF_SAMPLE_CALLCHAIN.
+const uint64_t* ReadCallchain(const uint64_t* array,
+ bool swap_bytes,
+ struct perf_sample* sample) {
+ // Make sure there is no existing allocated memory in |sample->callchain|.
+ CHECK_EQ(static_cast<void*>(NULL), sample->callchain);
+
+ // The callgraph data consists of a uint64_t value |nr| followed by |nr|
+ // addresses.
+ uint64_t callchain_size = *array++;
+ if (swap_bytes)
+ ByteSwap(&callchain_size);
+ struct ip_callchain* callchain =
+ reinterpret_cast<struct ip_callchain*>(new uint64_t[callchain_size + 1]);
+ callchain->nr = callchain_size;
+ for (size_t i = 0; i < callchain_size; ++i) {
+ callchain->ips[i] = *array++;
+ if (swap_bytes)
+ ByteSwap(&callchain->ips[i]);
+ }
+ sample->callchain = callchain;
+
+ return array;
+}
+
+// Read raw info from perf data. Corresponds to sample format type
+// PERF_SAMPLE_RAW.
+const uint64_t* ReadRawData(const uint64_t* array,
+ bool swap_bytes,
+ struct perf_sample* sample) {
+ // First read the size.
+ const uint32_t* ptr = reinterpret_cast<const uint32_t*>(array);
+ sample->raw_size = *ptr++;
+ if (swap_bytes)
+ ByteSwap(&sample->raw_size);
+
+ // Allocate space for and read the raw data bytes.
+ sample->raw_data = new uint8_t[sample->raw_size];
+ memcpy(sample->raw_data, ptr, sample->raw_size);
+
+ // Determine the bytes that were read, and align to the next 64 bits.
+ int bytes_read = AlignSize(sizeof(sample->raw_size) + sample->raw_size,
+ sizeof(uint64_t));
+ array += bytes_read / sizeof(uint64_t);
+
+ return array;
+}
+
+// Read call chain info from perf data. Corresponds to sample format type
+// PERF_SAMPLE_CALLCHAIN.
+const uint64_t* ReadBranchStack(const uint64_t* array,
+ bool swap_bytes,
+ struct perf_sample* sample) {
+ // Make sure there is no existing allocated memory in
+ // |sample->branch_stack|.
+ CHECK_EQ(static_cast<void*>(NULL), sample->branch_stack);
+
+ // The branch stack data consists of a uint64_t value |nr| followed by |nr|
+ // branch_entry structs.
+ uint64_t branch_stack_size = *array++;
+ if (swap_bytes)
+ ByteSwap(&branch_stack_size);
+ struct branch_stack* branch_stack =
+ reinterpret_cast<struct branch_stack*>(
+ new uint8_t[sizeof(uint64_t) +
+ branch_stack_size * sizeof(struct branch_entry)]);
+ branch_stack->nr = branch_stack_size;
+ for (size_t i = 0; i < branch_stack_size; ++i) {
+ memcpy(&branch_stack->entries[i], array, sizeof(struct branch_entry));
+ array += sizeof(struct branch_entry) / sizeof(*array);
+ if (swap_bytes) {
+ ByteSwap(&branch_stack->entries[i].from);
+ ByteSwap(&branch_stack->entries[i].to);
+ }
+ }
+ sample->branch_stack = branch_stack;
+
+ return array;
+}
+
+size_t ReadPerfSampleFromData(const perf_event_type event_type,
+ const uint64_t* array,
+ const uint64_t sample_fields,
+ const uint64_t read_format,
+ bool swap_bytes,
+ struct perf_sample* sample) {
+ const uint64_t* initial_array_ptr = array;
+
+ union {
+ uint32_t val32[sizeof(uint64_t) / sizeof(uint32_t)];
+ uint64_t val64;
+ };
+
+ // See structure for PERF_RECORD_SAMPLE in kernel/perf_event.h
+ // and compare sample_id when sample_id_all is set.
+
+ // NB: For sample_id, sample_fields has already been masked to the set
+ // of fields in that struct by GetSampleFieldsForEventType. That set
+ // of fields is mostly in the same order as PERF_RECORD_SAMPLE, with
+ // the exception of PERF_SAMPLE_IDENTIFIER.
+
+ // PERF_SAMPLE_IDENTIFIER is in a different location depending on
+ // if this is a SAMPLE event or the sample_id of another event.
+ if (event_type == PERF_RECORD_SAMPLE) {
+ // { u64 id; } && PERF_SAMPLE_IDENTIFIER
+ if (sample_fields & PERF_SAMPLE_IDENTIFIER) {
+ sample->id = MaybeSwap(*array++, swap_bytes);
+ }
+ }
+
+ // { u64 ip; } && PERF_SAMPLE_IP
+ if (sample_fields & PERF_SAMPLE_IP) {
+ sample->ip = MaybeSwap(*array++, swap_bytes);
+ }
+
+ // { u32 pid, tid; } && PERF_SAMPLE_TID
+ if (sample_fields & PERF_SAMPLE_TID) {
+ val64 = *array++;
+ sample->pid = MaybeSwap(val32[0], swap_bytes);
+ sample->tid = MaybeSwap(val32[1], swap_bytes);
+ }
+
+ // { u64 time; } && PERF_SAMPLE_TIME
+ if (sample_fields & PERF_SAMPLE_TIME) {
+ sample->time = MaybeSwap(*array++, swap_bytes);
+ }
+
+ // { u64 addr; } && PERF_SAMPLE_ADDR
+ if (sample_fields & PERF_SAMPLE_ADDR) {
+ sample->addr = MaybeSwap(*array++, swap_bytes);
+ }
+
+ // { u64 id; } && PERF_SAMPLE_ID
+ if (sample_fields & PERF_SAMPLE_ID) {
+ sample->id = MaybeSwap(*array++, swap_bytes);
+ }
+
+ // { u64 stream_id;} && PERF_SAMPLE_STREAM_ID
+ if (sample_fields & PERF_SAMPLE_STREAM_ID) {
+ sample->stream_id = MaybeSwap(*array++, swap_bytes);
+ }
+
+ // { u32 cpu, res; } && PERF_SAMPLE_CPU
+ if (sample_fields & PERF_SAMPLE_CPU) {
+ val64 = *array++;
+ sample->cpu = MaybeSwap(val32[0], swap_bytes);
+ // sample->res = MaybeSwap(*val32[1], swap_bytes); // not implemented?
+ }
+
+ // This is the location of PERF_SAMPLE_IDENTIFIER in struct sample_id.
+ if (event_type != PERF_RECORD_SAMPLE) {
+ // { u64 id; } && PERF_SAMPLE_IDENTIFIER
+ if (sample_fields & PERF_SAMPLE_IDENTIFIER) {
+ sample->id = MaybeSwap(*array++, swap_bytes);
+ }
+ }
+
+ //
+ // The remaining fields are only in PERF_RECORD_SAMPLE
+ //
+
+ // { u64 period; } && PERF_SAMPLE_PERIOD
+ if (sample_fields & PERF_SAMPLE_PERIOD) {
+ sample->period = MaybeSwap(*array++, swap_bytes);
+ }
+
+ // { struct read_format values; } && PERF_SAMPLE_READ
+ if (sample_fields & PERF_SAMPLE_READ) {
+ // TODO(cwp-team): support grouped read info.
+ if (read_format & PERF_FORMAT_GROUP)
+ return 0;
+ array = ReadReadInfo(array, swap_bytes, read_format, sample);
+ }
+
+ // { u64 nr,
+ // u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN
+ if (sample_fields & PERF_SAMPLE_CALLCHAIN) {
+ array = ReadCallchain(array, swap_bytes, sample);
+ }
+
+ // { u32 size;
+ // char data[size];}&& PERF_SAMPLE_RAW
+ if (sample_fields & PERF_SAMPLE_RAW) {
+ array = ReadRawData(array, swap_bytes, sample);
+ }
+
+ // { u64 nr;
+ // { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK
+ if (sample_fields & PERF_SAMPLE_BRANCH_STACK) {
+ array = ReadBranchStack(array, swap_bytes, sample);
+ }
+
+ static const u64 kUnimplementedSampleFields =
+ PERF_SAMPLE_REGS_USER |
+ PERF_SAMPLE_STACK_USER |
+ PERF_SAMPLE_WEIGHT |
+ PERF_SAMPLE_DATA_SRC |
+ PERF_SAMPLE_TRANSACTION;
+
+ if (sample_fields & kUnimplementedSampleFields) {
+ LOG(WARNING) << "Unimplemented sample fields 0x"
+ << std::hex << (sample_fields & kUnimplementedSampleFields);
+ }
+
+ if (sample_fields & ~(PERF_SAMPLE_MAX-1)) {
+ LOG(WARNING) << "Unrecognized sample fields 0x"
+ << std::hex << (sample_fields & ~(PERF_SAMPLE_MAX-1));
+ }
+
+ return (array - initial_array_ptr) * sizeof(uint64_t);
+}
+
+size_t WritePerfSampleToData(const perf_event_type event_type,
+ const struct perf_sample& sample,
+ const uint64_t sample_fields,
+ const uint64_t read_format,
+ uint64_t* array) {
+ const uint64_t* initial_array_ptr = array;
+
+ union {
+ uint32_t val32[sizeof(uint64_t) / sizeof(uint32_t)];
+ uint64_t val64;
+ };
+
+ // See notes at the top of ReadPerfSampleFromData regarding the structure
+ // of PERF_RECORD_SAMPLE, sample_id, and PERF_SAMPLE_IDENTIFIER, as they
+ // all apply here as well.
+
+ // PERF_SAMPLE_IDENTIFIER is in a different location depending on
+ // if this is a SAMPLE event or the sample_id of another event.
+ if (event_type == PERF_RECORD_SAMPLE) {
+ // { u64 id; } && PERF_SAMPLE_IDENTIFIER
+ if (sample_fields & PERF_SAMPLE_IDENTIFIER) {
+ *array++ = sample.id;
+ }
+ }
+
+ // { u64 ip; } && PERF_SAMPLE_IP
+ if (sample_fields & PERF_SAMPLE_IP) {
+ *array++ = sample.ip;
+ }
+
+ // { u32 pid, tid; } && PERF_SAMPLE_TID
+ if (sample_fields & PERF_SAMPLE_TID) {
+ val32[0] = sample.pid;
+ val32[1] = sample.tid;
+ *array++ = val64;
+ }
+
+ // { u64 time; } && PERF_SAMPLE_TIME
+ if (sample_fields & PERF_SAMPLE_TIME) {
+ *array++ = sample.time;
+ }
+
+ // { u64 addr; } && PERF_SAMPLE_ADDR
+ if (sample_fields & PERF_SAMPLE_ADDR) {
+ *array++ = sample.addr;
+ }
+
+ // { u64 id; } && PERF_SAMPLE_ID
+ if (sample_fields & PERF_SAMPLE_ID) {
+ *array++ = sample.id;
+ }
+
+ // { u64 stream_id;} && PERF_SAMPLE_STREAM_ID
+ if (sample_fields & PERF_SAMPLE_STREAM_ID) {
+ *array++ = sample.stream_id;
+ }
+
+ // { u32 cpu, res; } && PERF_SAMPLE_CPU
+ if (sample_fields & PERF_SAMPLE_CPU) {
+ val32[0] = sample.cpu;
+ // val32[1] = sample.res; // not implemented?
+ val32[1] = 0;
+ *array++ = val64;
+ }
+
+ // This is the location of PERF_SAMPLE_IDENTIFIER in struct sample_id.
+ if (event_type != PERF_RECORD_SAMPLE) {
+ // { u64 id; } && PERF_SAMPLE_IDENTIFIER
+ if (sample_fields & PERF_SAMPLE_IDENTIFIER) {
+ *array++ = sample.id;
+ }
+ }
+
+ //
+ // The remaining fields are only in PERF_RECORD_SAMPLE
+ //
+
+ // { u64 period; } && PERF_SAMPLE_PERIOD
+ if (sample_fields & PERF_SAMPLE_PERIOD) {
+ *array++ = sample.period;
+ }
+
+ // { struct read_format values; } && PERF_SAMPLE_READ
+ if (sample_fields & PERF_SAMPLE_READ) {
+ // TODO(cwp-team): support grouped read info.
+ if (read_format & PERF_FORMAT_GROUP)
+ return 0;
+ if (read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
+ *array++ = sample.read.time_enabled;
+ if (read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
+ *array++ = sample.read.time_running;
+ if (read_format & PERF_FORMAT_ID)
+ *array++ = sample.read.one.id;
+ }
+
+ // { u64 nr,
+ // u64 ips[nr]; } && PERF_SAMPLE_CALLCHAIN
+ if (sample_fields & PERF_SAMPLE_CALLCHAIN) {
+ if (!sample.callchain) {
+ LOG(ERROR) << "Expecting callchain data, but none was found.";
+ } else {
+ *array++ = sample.callchain->nr;
+ for (size_t i = 0; i < sample.callchain->nr; ++i)
+ *array++ = sample.callchain->ips[i];
+ }
+ }
+
+ // { u32 size;
+ // char data[size];}&& PERF_SAMPLE_RAW
+ if (sample_fields & PERF_SAMPLE_RAW) {
+ uint32_t* ptr = reinterpret_cast<uint32_t*>(array);
+ *ptr++ = sample.raw_size;
+ memcpy(ptr, sample.raw_data, sample.raw_size);
+
+ // Update the data read pointer after aligning to the next 64 bytes.
+ int num_bytes = AlignSize(sizeof(sample.raw_size) + sample.raw_size,
+ sizeof(uint64_t));
+ array += num_bytes / sizeof(uint64_t);
+ }
+
+ // { u64 nr;
+ // { u64 from, to, flags } lbr[nr];} && PERF_SAMPLE_BRANCH_STACK
+ if (sample_fields & PERF_SAMPLE_BRANCH_STACK) {
+ if (!sample.branch_stack) {
+ LOG(ERROR) << "Expecting branch stack data, but none was found.";
+ } else {
+ *array++ = sample.branch_stack->nr;
+ for (size_t i = 0; i < sample.branch_stack->nr; ++i) {
+ *array++ = sample.branch_stack->entries[i].from;
+ *array++ = sample.branch_stack->entries[i].to;
+ memcpy(array++, &sample.branch_stack->entries[i].flags,
+ sizeof(uint64_t));
+ }
+ }
+ }
+
+ return (array - initial_array_ptr) * sizeof(uint64_t);
+}
+
+} // namespace
+
+PerfReader::~PerfReader() {
+ // Free allocated memory.
+ for (size_t i = 0; i < build_id_events_.size(); ++i)
+ if (build_id_events_[i])
+ free(build_id_events_[i]);
+}
+
+void PerfReader::PerfizeBuildIDString(string* build_id) {
+ build_id->resize(kBuildIDStringLength, '0');
+}
+
+void PerfReader::UnperfizeBuildIDString(string* build_id) {
+ const size_t kPaddingSize = 8;
+ const string kBuildIDPadding = string(kPaddingSize, '0');
+
+ // Remove kBuildIDPadding from the end of build_id until we cannot remove any
+ // more, or removing more would cause the build id to be empty.
+ while (build_id->size() > kPaddingSize &&
+ build_id->substr(build_id->size() - kPaddingSize) == kBuildIDPadding) {
+ build_id->resize(build_id->size() - kPaddingSize);
+ }
+}
+
+bool PerfReader::ReadFile(const string& filename) {
+ std::vector<char> data;
+ if (!ReadFileToData(filename, &data))
+ return false;
+ return ReadFromVector(data);
+}
+
+bool PerfReader::ReadFromVector(const std::vector<char>& data) {
+ return ReadFromPointer(&data[0], data.size());
+}
+
+bool PerfReader::ReadFromString(const string& str) {
+ return ReadFromPointer(str.c_str(), str.size());
+}
+
+bool PerfReader::ReadFromPointer(const char* perf_data, size_t size) {
+ const ConstBufferWithSize data = { perf_data, size };
+
+ if (data.size == 0)
+ return false;
+ if (!ReadHeader(data))
+ return false;
+
+ // Check if it is normal perf data.
+ if (header_.size == sizeof(header_)) {
+ DLOG(INFO) << "Perf data is in normal format.";
+ metadata_mask_ = header_.adds_features[0];
+ return (ReadAttrs(data) && ReadEventTypes(data) && ReadData(data)
+ && ReadMetadata(data));
+ }
+
+ // Otherwise it is piped data.
+ LOG(ERROR) << "Internal error: no support for piped data";
+ return false;
+}
+
+bool PerfReader::Localize(
+ const std::map<string, string>& build_ids_to_filenames) {
+ std::map<string, string> perfized_build_ids_to_filenames;
+ std::map<string, string>::const_iterator it;
+ for (it = build_ids_to_filenames.begin();
+ it != build_ids_to_filenames.end();
+ ++it) {
+ string build_id = it->first;
+ PerfizeBuildIDString(&build_id);
+ perfized_build_ids_to_filenames[build_id] = it->second;
+ }
+
+ std::map<string, string> filename_map;
+ for (size_t i = 0; i < build_id_events_.size(); ++i) {
+ build_id_event* event = build_id_events_[i];
+ string build_id = HexToString(event->build_id, kBuildIDArraySize);
+ if (perfized_build_ids_to_filenames.find(build_id) ==
+ perfized_build_ids_to_filenames.end()) {
+ continue;
+ }
+
+ string new_name = perfized_build_ids_to_filenames.at(build_id);
+ filename_map[string(event->filename)] = new_name;
+ build_id_event* new_event = CreateOrUpdateBuildID("", new_name, 0, event);
+ CHECK(new_event);
+ build_id_events_[i] = new_event;
+ }
+
+ LocalizeUsingFilenames(filename_map);
+ return true;
+}
+
+bool PerfReader::LocalizeUsingFilenames(
+ const std::map<string, string>& filename_map) {
+ LocalizeMMapFilenames(filename_map);
+ for (size_t i = 0; i < build_id_events_.size(); ++i) {
+ build_id_event* event = build_id_events_[i];
+ string old_name = event->filename;
+
+ if (filename_map.find(event->filename) != filename_map.end()) {
+ const string& new_name = filename_map.at(old_name);
+ build_id_event* new_event = CreateOrUpdateBuildID("", new_name, 0, event);
+ CHECK(new_event);
+ build_id_events_[i] = new_event;
+ }
+ }
+ return true;
+}
+
+void PerfReader::GetFilenames(std::vector<string>* filenames) const {
+ std::set<string> filename_set;
+ GetFilenamesAsSet(&filename_set);
+ filenames->clear();
+ filenames->insert(filenames->begin(), filename_set.begin(),
+ filename_set.end());
+}
+
+void PerfReader::GetFilenamesAsSet(std::set<string>* filenames) const {
+ filenames->clear();
+ for (size_t i = 0; i < events_.size(); ++i) {
+ const event_t& event = *events_[i];
+ if (event.header.type == PERF_RECORD_MMAP)
+ filenames->insert(event.mmap.filename);
+ if (event.header.type == PERF_RECORD_MMAP2)
+ filenames->insert(event.mmap2.filename);
+ }
+}
+
+void PerfReader::GetFilenamesToBuildIDs(
+ std::map<string, string>* filenames_to_build_ids) const {
+ filenames_to_build_ids->clear();
+ for (size_t i = 0; i < build_id_events_.size(); ++i) {
+ const build_id_event& event = *build_id_events_[i];
+ string build_id = HexToString(event.build_id, kBuildIDArraySize);
+ (*filenames_to_build_ids)[event.filename] = build_id;
+ }
+}
+
+bool PerfReader::IsSupportedEventType(uint32_t type) {
+ switch (type) {
+ case PERF_RECORD_SAMPLE:
+ case PERF_RECORD_MMAP:
+ case PERF_RECORD_MMAP2:
+ case PERF_RECORD_FORK:
+ case PERF_RECORD_EXIT:
+ case PERF_RECORD_COMM:
+ case PERF_RECORD_LOST:
+ case PERF_RECORD_THROTTLE:
+ case PERF_RECORD_UNTHROTTLE:
+ return true;
+ case PERF_RECORD_READ:
+ case PERF_RECORD_MAX:
+ return false;
+ default:
+ LOG(FATAL) << "Unknown event type " << type;
+ return false;
+ }
+}
+
+bool PerfReader::ReadPerfSampleInfo(const event_t& event,
+ struct perf_sample* sample) const {
+ CHECK(sample);
+
+ if (!IsSupportedEventType(event.header.type)) {
+ LOG(ERROR) << "Unsupported event type " << event.header.type;
+ return false;
+ }
+
+ uint64_t sample_format = GetSampleFieldsForEventType(event.header.type,
+ sample_type_);
+ uint64_t offset = GetPerfSampleDataOffset(event);
+ size_t size_read = ReadPerfSampleFromData(
+ static_cast<perf_event_type>(event.header.type),
+ reinterpret_cast<const uint64_t*>(&event) + offset / sizeof(uint64_t),
+ sample_format,
+ read_format_,
+ is_cross_endian_,
+ sample);
+
+ size_t expected_size = event.header.size - offset;
+ if (size_read != expected_size) {
+ LOG(ERROR) << "Read " << size_read << " bytes, expected "
+ << expected_size << " bytes.";
+ }
+
+ return (size_read == expected_size);
+}
+
+bool PerfReader::WritePerfSampleInfo(const perf_sample& sample,
+ event_t* event) const {
+ CHECK(event);
+
+ if (!IsSupportedEventType(event->header.type)) {
+ LOG(ERROR) << "Unsupported event type " << event->header.type;
+ return false;
+ }
+
+ uint64_t sample_format = GetSampleFieldsForEventType(event->header.type,
+ sample_type_);
+ uint64_t offset = GetPerfSampleDataOffset(*event);
+
+ size_t expected_size = event->header.size - offset;
+ memset(reinterpret_cast<uint8_t*>(event) + offset, 0, expected_size);
+ size_t size_written = WritePerfSampleToData(
+ static_cast<perf_event_type>(event->header.type),
+ sample,
+ sample_format,
+ read_format_,
+ reinterpret_cast<uint64_t*>(event) + offset / sizeof(uint64_t));
+ if (size_written != expected_size) {
+ LOG(ERROR) << "Wrote " << size_written << " bytes, expected "
+ << expected_size << " bytes.";
+ }
+
+ return (size_written == expected_size);
+}
+
+bool PerfReader::ReadHeader(const ConstBufferWithSize& data) {
+ CheckNoEventHeaderPadding();
+ size_t offset = 0;
+ if (!ReadDataFromBuffer(data, sizeof(piped_header_), "header magic",
+ &offset, &piped_header_)) {
+ return false;
+ }
+ if (piped_header_.magic != kPerfMagic &&
+ piped_header_.magic != bswap_64(kPerfMagic)) {
+ LOG(ERROR) << "Read wrong magic. Expected: 0x" << std::hex << kPerfMagic
+ << " or 0x" << std::hex << bswap_64(kPerfMagic)
+ << " Got: 0x" << std::hex << piped_header_.magic;
+ return false;
+ }
+ is_cross_endian_ = (piped_header_.magic != kPerfMagic);
+ if (is_cross_endian_)
+ ByteSwap(&piped_header_.size);
+
+ // Header can be a piped header.
+ if (piped_header_.size == sizeof(piped_header_))
+ return true;
+
+ // Re-read full header
+ offset = 0;
+ if (!ReadDataFromBuffer(data, sizeof(header_), "header data",
+ &offset, &header_)) {
+ return false;
+ }
+ if (is_cross_endian_)
+ ByteSwap(&header_.size);
+
+ DLOG(INFO) << "event_types.size: " << header_.event_types.size;
+ DLOG(INFO) << "event_types.offset: " << header_.event_types.offset;
+
+ return true;
+}
+
+bool PerfReader::ReadAttrs(const ConstBufferWithSize& data) {
+ size_t num_attrs = header_.attrs.size / header_.attr_size;
+ size_t offset = header_.attrs.offset;
+ for (size_t i = 0; i < num_attrs; i++) {
+ if (!ReadAttr(data, &offset))
+ return false;
+ }
+ return true;
+}
+
+bool PerfReader::ReadAttr(const ConstBufferWithSize& data, size_t* offset) {
+ PerfFileAttr attr;
+ if (!ReadEventAttr(data, offset, &attr.attr))
+ return false;
+
+ perf_file_section ids;
+ if (!ReadDataFromBuffer(data, sizeof(ids), "ID section info", offset, &ids))
+ return false;
+ if (is_cross_endian_) {
+ ByteSwap(&ids.offset);
+ ByteSwap(&ids.size);
+ }
+
+ size_t num_ids = ids.size / sizeof(decltype(attr.ids)::value_type);
+ // Convert the offset from u64 to size_t.
+ size_t ids_offset = ids.offset;
+ if (!ReadUniqueIDs(data, num_ids, &ids_offset, &attr.ids))
+ return false;
+ attrs_.push_back(attr);
+ return true;
+}
+
+u32 PerfReader::ReadPerfEventAttrSize(const ConstBufferWithSize& data,
+ size_t attr_offset) {
+ static_assert(std::is_same<decltype(perf_event_attr::size), u32>::value,
+ "ReadPerfEventAttrSize return type should match "
+ "perf_event_attr.size");
+ u32 attr_size;
+ size_t attr_size_offset = attr_offset + offsetof(perf_event_attr, size);
+ if (!ReadDataFromBuffer(data, sizeof(perf_event_attr::size),
+ "attr.size", &attr_size_offset, &attr_size)) {
+ return kuint32max;
+ }
+ return MaybeSwap(attr_size, is_cross_endian_);
+}
+
+bool PerfReader::ReadEventAttr(const ConstBufferWithSize& data, size_t* offset,
+ perf_event_attr* attr) {
+ CheckNoPerfEventAttrPadding();
+
+ std::memset(attr, 0, sizeof(*attr));
+ //*attr = {0};
+
+ // read just size first
+ u32 attr_size = ReadPerfEventAttrSize(data, *offset);
+ if (attr_size == kuint32max) {
+ return false;
+ }
+
+ // now read the the struct.
+ if (!ReadDataFromBuffer(data, attr_size, "attribute", offset,
+ reinterpret_cast<char*>(attr))) {
+ return false;
+ }
+
+ if (is_cross_endian_) {
+ // Depending on attr->size, some of these might not have actually been
+ // read. This is okay: they are zero.
+ ByteSwap(&attr->type);
+ ByteSwap(&attr->size);
+ ByteSwap(&attr->config);
+ ByteSwap(&attr->sample_period);
+ ByteSwap(&attr->sample_type);
+ ByteSwap(&attr->read_format);
+
+ // NB: This will also reverse precise_ip : 2 as if it was two fields:
+ auto *const bitfield_start = &attr->read_format + 1;
+ SwapBitfieldOfBits(reinterpret_cast<u8*>(bitfield_start),
+ sizeof(u64));
+ // ... So swap it back:
+ const auto tmp = attr->precise_ip;
+ attr->precise_ip = (tmp & 0x2) >> 1 | (tmp & 0x1) << 1;
+
+ ByteSwap(&attr->wakeup_events); // union with wakeup_watermark
+ ByteSwap(&attr->bp_type);
+ ByteSwap(&attr->bp_addr); // union with config1
+ ByteSwap(&attr->bp_len); // union with config2
+ ByteSwap(&attr->branch_sample_type);
+ ByteSwap(&attr->sample_regs_user);
+ ByteSwap(&attr->sample_stack_user);
+ }
+
+ CHECK_EQ(attr_size, attr->size);
+ // The actual perf_event_attr data size might be different from the size of
+ // the struct definition. Check against perf_event_attr's |size| field.
+ attr->size = sizeof(*attr);
+
+ // Assign sample type if it hasn't been assigned, otherwise make sure all
+ // subsequent attributes have the same sample type bits set.
+ if (sample_type_ == 0) {
+ sample_type_ = attr->sample_type;
+ } else {
+ CHECK_EQ(sample_type_, attr->sample_type)
+ << "Event type sample format does not match sample format of other "
+ << "event type.";
+ }
+
+ if (read_format_ == 0) {
+ read_format_ = attr->read_format;
+ } else {
+ CHECK_EQ(read_format_, attr->read_format)
+ << "Event type read format does not match read format of other event "
+ << "types.";
+ }
+
+ return true;
+}
+
+bool PerfReader::ReadUniqueIDs(const ConstBufferWithSize& data, size_t num_ids,
+ size_t* offset, std::vector<u64>* ids) {
+ ids->resize(num_ids);
+ for (size_t j = 0; j < num_ids; j++) {
+ if (!ReadDataFromBuffer(data, sizeof(ids->at(j)), "ID", offset,
+ &ids->at(j))) {
+ return false;
+ }
+ if (is_cross_endian_)
+ ByteSwap(&ids->at(j));
+ }
+ return true;
+}
+
+bool PerfReader::ReadEventTypes(const ConstBufferWithSize& data) {
+ size_t num_event_types = header_.event_types.size /
+ sizeof(struct perf_trace_event_type);
+ CHECK_EQ(sizeof(perf_trace_event_type) * num_event_types,
+ header_.event_types.size);
+ size_t offset = header_.event_types.offset;
+ for (size_t i = 0; i < num_event_types; ++i) {
+ if (!ReadEventType(data, &offset))
+ return false;
+ }
+ return true;
+}
+
+bool PerfReader::ReadEventType(const ConstBufferWithSize& data,
+ size_t* offset) {
+ CheckNoEventTypePadding();
+ perf_trace_event_type type;
+ memset(&type, 0, sizeof(type));
+ if (!ReadDataFromBuffer(data, sizeof(type.event_id), "event id",
+ offset, &type.event_id)) {
+ return false;
+ }
+ const char* event_name = reinterpret_cast<const char*>(data.ptr + *offset);
+ CHECK_GT(snprintf(type.name, sizeof(type.name), "%s", event_name), 0);
+ *offset += sizeof(type.name);
+ event_types_.push_back(type);
+ return true;
+}
+
+bool PerfReader::ReadData(const ConstBufferWithSize& data) {
+ u64 data_remaining_bytes = header_.data.size;
+ size_t offset = header_.data.offset;
+ while (data_remaining_bytes != 0) {
+ if (data.size < offset) {
+ LOG(ERROR) << "Not enough data to read a perf event.";
+ return false;
+ }
+
+ const event_t* event = reinterpret_cast<const event_t*>(data.ptr + offset);
+ if (!ReadPerfEventBlock(*event))
+ return false;
+ data_remaining_bytes -= event->header.size;
+ offset += event->header.size;
+ }
+
+ DLOG(INFO) << "Number of events stored: "<< events_.size();
+ return true;
+}
+
+bool PerfReader::ReadMetadata(const ConstBufferWithSize& data) {
+ size_t offset = header_.data.offset + header_.data.size;
+
+ for (u32 type = HEADER_FIRST_FEATURE; type != HEADER_LAST_FEATURE; ++type) {
+ if ((metadata_mask_ & (1 << type)) == 0)
+ continue;
+
+ if (data.size < offset) {
+ LOG(ERROR) << "Not enough data to read offset and size of metadata.";
+ return false;
+ }
+
+ u64 metadata_offset, metadata_size;
+ if (!ReadDataFromBuffer(data, sizeof(metadata_offset), "metadata offset",
+ &offset, &metadata_offset) ||
+ !ReadDataFromBuffer(data, sizeof(metadata_size), "metadata size",
+ &offset, &metadata_size)) {
+ return false;
+ }
+
+ if (data.size < metadata_offset + metadata_size) {
+ LOG(ERROR) << "Not enough data to read metadata.";
+ return false;
+ }
+
+ switch (type) {
+ case HEADER_TRACING_DATA:
+ if (!ReadTracingMetadata(data, metadata_offset, metadata_size)) {
+ return false;
+ }
+ break;
+ case HEADER_BUILD_ID:
+ if (!ReadBuildIDMetadata(data, type, metadata_offset, metadata_size))
+ return false;
+ break;
+ case HEADER_HOSTNAME:
+ case HEADER_OSRELEASE:
+ case HEADER_VERSION:
+ case HEADER_ARCH:
+ case HEADER_CPUDESC:
+ case HEADER_CPUID:
+ case HEADER_CMDLINE:
+ if (!ReadStringMetadata(data, type, metadata_offset, metadata_size))
+ return false;
+ break;
+ case HEADER_NRCPUS:
+ if (!ReadUint32Metadata(data, type, metadata_offset, metadata_size))
+ return false;
+ break;
+ case HEADER_TOTAL_MEM:
+ if (!ReadUint64Metadata(data, type, metadata_offset, metadata_size))
+ return false;
+ break;
+ case HEADER_EVENT_DESC:
+ break;
+ case HEADER_CPU_TOPOLOGY:
+ if (!ReadCPUTopologyMetadata(data, type, metadata_offset, metadata_size))
+ return false;
+ break;
+ case HEADER_NUMA_TOPOLOGY:
+ if (!ReadNUMATopologyMetadata(data, type, metadata_offset, metadata_size))
+ return false;
+ break;
+ case HEADER_PMU_MAPPINGS:
+ // ignore for now
+ continue;
+ break;
+ case HEADER_BRANCH_STACK:
+ continue;
+ default: LOG(INFO) << "Unsupported metadata type: " << type;
+ break;
+ }
+ }
+
+ // Event type events are optional in some newer versions of perf. They
+ // contain the same information that is already in |attrs_|. Make sure the
+ // number of event types matches the number of attrs, but only if there are
+ // event type events present.
+ if (event_types_.size() > 0) {
+ if (event_types_.size() != attrs_.size()) {
+ LOG(ERROR) << "Mismatch between number of event type events and attr "
+ << "events: " << event_types_.size() << " vs "
+ << attrs_.size();
+ return false;
+ }
+ metadata_mask_ |= (1 << HEADER_EVENT_DESC);
+ }
+ return true;
+}
+
+bool PerfReader::ReadBuildIDMetadata(const ConstBufferWithSize& data, u32 /*type*/,
+ size_t offset, size_t size) {
+ CheckNoBuildIDEventPadding();
+ while (size > 0) {
+ // Make sure there is enough data for everything but the filename.
+ if (data.size < offset + sizeof(build_id_event) / sizeof(*data.ptr)) {
+ LOG(ERROR) << "Not enough bytes to read build id event";
+ return false;
+ }
+
+ const build_id_event* temp_ptr =
+ reinterpret_cast<const build_id_event*>(data.ptr + offset);
+ u16 event_size = temp_ptr->header.size;
+ if (is_cross_endian_)
+ ByteSwap(&event_size);
+
+ // Make sure there is enough data for the rest of the event.
+ if (data.size < offset + event_size / sizeof(*data.ptr)) {
+ LOG(ERROR) << "Not enough bytes to read build id event";
+ return false;
+ }
+
+ // Allocate memory for the event and copy over the bytes.
+ build_id_event* event = CallocMemoryForBuildID(event_size);
+ if (!ReadDataFromBuffer(data, event_size, "build id event",
+ &offset, event)) {
+ return false;
+ }
+ if (is_cross_endian_) {
+ ByteSwap(&event->header.type);
+ ByteSwap(&event->header.misc);
+ ByteSwap(&event->header.size);
+ ByteSwap(&event->pid);
+ }
+ size -= event_size;
+
+ // Perf tends to use more space than necessary, so fix the size.
+ event->header.size =
+ sizeof(*event) + GetUint64AlignedStringLength(event->filename);
+ build_id_events_.push_back(event);
+ }
+
+ return true;
+}
+
+bool PerfReader::ReadStringMetadata(const ConstBufferWithSize& data, u32 type,
+ size_t offset, size_t size) {
+ PerfStringMetadata str_data;
+ str_data.type = type;
+
+ size_t start_offset = offset;
+ // Skip the number of string data if it is present.
+ if (NeedsNumberOfStringData(type))
+ offset += sizeof(num_string_data_type) / sizeof(*data.ptr);
+
+ while ((offset - start_offset) < size) {
+ CStringWithLength single_string;
+ if (!ReadStringFromBuffer(data, is_cross_endian_, &offset, &single_string))
+ return false;
+ str_data.data.push_back(single_string);
+ }
+
+ string_metadata_.push_back(str_data);
+ return true;
+}
+
+bool PerfReader::ReadUint32Metadata(const ConstBufferWithSize& data, u32 type,
+ size_t offset, size_t size) {
+ PerfUint32Metadata uint32_data;
+ uint32_data.type = type;
+
+ size_t start_offset = offset;
+ while (size > offset - start_offset) {
+ uint32_t item;
+ if (!ReadDataFromBuffer(data, sizeof(item), "uint32_t data", &offset,
+ &item))
+ return false;
+
+ if (is_cross_endian_)
+ ByteSwap(&item);
+
+ uint32_data.data.push_back(item);
+ }
+
+ uint32_metadata_.push_back(uint32_data);
+ return true;
+}
+
+bool PerfReader::ReadUint64Metadata(const ConstBufferWithSize& data, u32 type,
+ size_t offset, size_t size) {
+ PerfUint64Metadata uint64_data;
+ uint64_data.type = type;
+
+ size_t start_offset = offset;
+ while (size > offset - start_offset) {
+ uint64_t item;
+ if (!ReadDataFromBuffer(data, sizeof(item), "uint64_t data", &offset,
+ &item))
+ return false;
+
+ if (is_cross_endian_)
+ ByteSwap(&item);
+
+ uint64_data.data.push_back(item);
+ }
+
+ uint64_metadata_.push_back(uint64_data);
+ return true;
+}
+
+bool PerfReader::ReadCPUTopologyMetadata(
+ const ConstBufferWithSize& data, u32 /*type*/, size_t offset, size_t /*size*/) {
+ num_siblings_type num_core_siblings;
+ if (!ReadDataFromBuffer(data, sizeof(num_core_siblings), "num cores",
+ &offset, &num_core_siblings)) {
+ return false;
+ }
+ if (is_cross_endian_)
+ ByteSwap(&num_core_siblings);
+
+ cpu_topology_.core_siblings.resize(num_core_siblings);
+ for (size_t i = 0; i < num_core_siblings; ++i) {
+ if (!ReadStringFromBuffer(data, is_cross_endian_, &offset,
+ &cpu_topology_.core_siblings[i])) {
+ return false;
+ }
+ }
+
+ num_siblings_type num_thread_siblings;
+ if (!ReadDataFromBuffer(data, sizeof(num_thread_siblings), "num threads",
+ &offset, &num_thread_siblings)) {
+ return false;
+ }
+ if (is_cross_endian_)
+ ByteSwap(&num_thread_siblings);
+
+ cpu_topology_.thread_siblings.resize(num_thread_siblings);
+ for (size_t i = 0; i < num_thread_siblings; ++i) {
+ if (!ReadStringFromBuffer(data, is_cross_endian_, &offset,
+ &cpu_topology_.thread_siblings[i])) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool PerfReader::ReadNUMATopologyMetadata(
+ const ConstBufferWithSize& data, u32 /*type*/, size_t offset, size_t /*size*/) {
+ numa_topology_num_nodes_type num_nodes;
+ if (!ReadDataFromBuffer(data, sizeof(num_nodes), "num nodes",
+ &offset, &num_nodes)) {
+ return false;
+ }
+ if (is_cross_endian_)
+ ByteSwap(&num_nodes);
+
+ for (size_t i = 0; i < num_nodes; ++i) {
+ PerfNodeTopologyMetadata node;
+ if (!ReadDataFromBuffer(data, sizeof(node.id), "node id",
+ &offset, &node.id) ||
+ !ReadDataFromBuffer(data, sizeof(node.total_memory),
+ "node total memory", &offset,
+ &node.total_memory) ||
+ !ReadDataFromBuffer(data, sizeof(node.free_memory),
+ "node free memory", &offset, &node.free_memory) ||
+ !ReadStringFromBuffer(data, is_cross_endian_, &offset,
+ &node.cpu_list)) {
+ return false;
+ }
+ if (is_cross_endian_) {
+ ByteSwap(&node.id);
+ ByteSwap(&node.total_memory);
+ ByteSwap(&node.free_memory);
+ }
+ numa_topology_.push_back(node);
+ }
+ return true;
+}
+
+bool PerfReader::ReadTracingMetadata(
+ const ConstBufferWithSize& data, size_t offset, size_t size) {
+ size_t tracing_data_offset = offset;
+ tracing_data_.resize(size);
+ return ReadDataFromBuffer(data, tracing_data_.size(), "tracing_data",
+ &tracing_data_offset, tracing_data_.data());
+}
+
+bool PerfReader::ReadTracingMetadataEvent(
+ const ConstBufferWithSize& data, size_t offset) {
+ // TRACING_DATA's header.size is a lie. It is the size of only the event
+ // struct. The size of the data is in the event struct, and followed
+ // immediately by the tracing header data.
+
+ // Make a copy of the event (but not the tracing data)
+ tracing_data_event tracing_event =
+ *reinterpret_cast<const tracing_data_event*>(data.ptr + offset);
+
+ if (is_cross_endian_) {
+ ByteSwap(&tracing_event.header.type);
+ ByteSwap(&tracing_event.header.misc);
+ ByteSwap(&tracing_event.header.size);
+ ByteSwap(&tracing_event.size);
+ }
+
+ return ReadTracingMetadata(data, offset + tracing_event.header.size,
+ tracing_event.size);
+}
+
+bool PerfReader::ReadAttrEventBlock(const ConstBufferWithSize& data,
+ size_t offset, size_t size) {
+ const size_t initial_offset = offset;
+ PerfFileAttr attr;
+ if (!ReadEventAttr(data, &offset, &attr.attr))
+ return false;
+
+ // attr.attr.size has been upgraded to the current size of perf_event_attr.
+ const size_t actual_attr_size = offset - initial_offset;
+
+ const size_t num_ids =
+ (size - actual_attr_size) / sizeof(decltype(attr.ids)::value_type);
+ if (!ReadUniqueIDs(data, num_ids, &offset, &attr.ids))
+ return false;
+
+ // Event types are found many times in the perf data file.
+ // Only add this event type if it is not already present.
+ for (size_t i = 0; i < attrs_.size(); ++i) {
+ if (attrs_[i].ids[0] == attr.ids[0])
+ return true;
+ }
+ attrs_.push_back(attr);
+ return true;
+}
+
+// When this method is called, |event| is a reference to the bytes in the data
+// vector that contains the entire perf.data file. As a result, we need to be
+// careful to only copy event.header.size bytes.
+// In particular, something like
+// event_t event_copy = event;
+// would be bad, because it would read past the end of the event, and possibly
+// pass the end of the data vector as well.
+bool PerfReader::ReadPerfEventBlock(const event_t& event) {
+ u16 size = event.header.size;
+ if (is_cross_endian_)
+ ByteSwap(&size);
+
+ if (size > sizeof(event_t)) {
+ LOG(INFO) << "Data size: " << size << " sizeof(event_t): "
+ << sizeof(event_t);
+ return false;
+ }
+
+ // Copy only the part of the event that is needed.
+ malloced_unique_ptr<event_t> event_copy(CallocMemoryForEvent(size));
+ memcpy(event_copy.get(), &event, size);
+ if (is_cross_endian_) {
+ ByteSwap(&event_copy->header.type);
+ ByteSwap(&event_copy->header.misc);
+ ByteSwap(&event_copy->header.size);
+ }
+
+ uint32_t type = event_copy->header.type;
+ if (is_cross_endian_) {
+ switch (type) {
+ case PERF_RECORD_SAMPLE:
+ break;
+ case PERF_RECORD_MMAP:
+ ByteSwap(&event_copy->mmap.pid);
+ ByteSwap(&event_copy->mmap.tid);
+ ByteSwap(&event_copy->mmap.start);
+ ByteSwap(&event_copy->mmap.len);
+ ByteSwap(&event_copy->mmap.pgoff);
+ break;
+ case PERF_RECORD_MMAP2:
+ ByteSwap(&event_copy->mmap2.pid);
+ ByteSwap(&event_copy->mmap2.tid);
+ ByteSwap(&event_copy->mmap2.start);
+ ByteSwap(&event_copy->mmap2.len);
+ ByteSwap(&event_copy->mmap2.pgoff);
+ ByteSwap(&event_copy->mmap2.maj);
+ ByteSwap(&event_copy->mmap2.min);
+ ByteSwap(&event_copy->mmap2.ino);
+ ByteSwap(&event_copy->mmap2.ino_generation);
+ break;
+ case PERF_RECORD_FORK:
+ case PERF_RECORD_EXIT:
+ ByteSwap(&event_copy->fork.pid);
+ ByteSwap(&event_copy->fork.tid);
+ ByteSwap(&event_copy->fork.ppid);
+ ByteSwap(&event_copy->fork.ptid);
+ break;
+ case PERF_RECORD_COMM:
+ ByteSwap(&event_copy->comm.pid);
+ ByteSwap(&event_copy->comm.tid);
+ break;
+ case PERF_RECORD_LOST:
+ ByteSwap(&event_copy->lost.id);
+ ByteSwap(&event_copy->lost.lost);
+ break;
+ case PERF_RECORD_READ:
+ ByteSwap(&event_copy->read.pid);
+ ByteSwap(&event_copy->read.tid);
+ ByteSwap(&event_copy->read.value);
+ ByteSwap(&event_copy->read.time_enabled);
+ ByteSwap(&event_copy->read.time_running);
+ ByteSwap(&event_copy->read.id);
+ break;
+ default:
+ LOG(FATAL) << "Unknown event type: " << type;
+ }
+ }
+
+ events_.push_back(std::move(event_copy));
+
+ return true;
+}
+
+size_t PerfReader::GetNumMetadata() const {
+ // This is just the number of 1s in the binary representation of the metadata
+ // mask. However, make sure to only use supported metadata, and don't include
+ // branch stack (since it doesn't have an entry in the metadata section).
+ uint64_t new_mask = metadata_mask_;
+ new_mask &= kSupportedMetadataMask & ~(1 << HEADER_BRANCH_STACK);
+ std::bitset<sizeof(new_mask) * CHAR_BIT> bits(new_mask);
+ return bits.count();
+}
+
+size_t PerfReader::GetEventDescMetadataSize() const {
+ size_t size = 0;
+ if (event_types_.empty()) {
+ return size;
+ }
+ if (metadata_mask_ & (1 << HEADER_EVENT_DESC)) {
+ if (event_types_.size() > 0 && event_types_.size() != attrs_.size()) {
+ LOG(ERROR) << "Mismatch between number of event type events and attr "
+ << "events: " << event_types_.size() << " vs "
+ << attrs_.size();
+ return size;
+ }
+ size += sizeof(event_desc_num_events) + sizeof(event_desc_attr_size);
+ CStringWithLength dummy;
+ for (size_t i = 0; i < attrs_.size(); ++i) {
+ size += sizeof(perf_event_attr) + sizeof(dummy.len);
+ size += sizeof(event_desc_num_unique_ids);
+ size += GetUint64AlignedStringLength(event_types_[i].name) * sizeof(char);
+ size += attrs_[i].ids.size() * sizeof(attrs_[i].ids[0]);
+ }
+ }
+ return size;
+}
+
+size_t PerfReader::GetBuildIDMetadataSize() const {
+ size_t size = 0;
+ for (size_t i = 0; i < build_id_events_.size(); ++i)
+ size += build_id_events_[i]->header.size;
+ return size;
+}
+
+size_t PerfReader::GetStringMetadataSize() const {
+ size_t size = 0;
+ for (size_t i = 0; i < string_metadata_.size(); ++i) {
+ const PerfStringMetadata& metadata = string_metadata_[i];
+ if (NeedsNumberOfStringData(metadata.type))
+ size += sizeof(num_string_data_type);
+
+ for (size_t j = 0; j < metadata.data.size(); ++j) {
+ const CStringWithLength& str = metadata.data[j];
+ size += sizeof(str.len) + (str.len * sizeof(char));
+ }
+ }
+ return size;
+}
+
+size_t PerfReader::GetUint32MetadataSize() const {
+ size_t size = 0;
+ for (size_t i = 0; i < uint32_metadata_.size(); ++i) {
+ const PerfUint32Metadata& metadata = uint32_metadata_[i];
+ size += metadata.data.size() * sizeof(metadata.data[0]);
+ }
+ return size;
+}
+
+size_t PerfReader::GetUint64MetadataSize() const {
+ size_t size = 0;
+ for (size_t i = 0; i < uint64_metadata_.size(); ++i) {
+ const PerfUint64Metadata& metadata = uint64_metadata_[i];
+ size += metadata.data.size() * sizeof(metadata.data[0]);
+ }
+ return size;
+}
+
+size_t PerfReader::GetCPUTopologyMetadataSize() const {
+ // Core siblings.
+ size_t size = sizeof(num_siblings_type);
+ for (size_t i = 0; i < cpu_topology_.core_siblings.size(); ++i) {
+ const CStringWithLength& str = cpu_topology_.core_siblings[i];
+ size += sizeof(str.len) + (str.len * sizeof(char));
+ }
+
+ // Thread siblings.
+ size += sizeof(num_siblings_type);
+ for (size_t i = 0; i < cpu_topology_.thread_siblings.size(); ++i) {
+ const CStringWithLength& str = cpu_topology_.thread_siblings[i];
+ size += sizeof(str.len) + (str.len * sizeof(char));
+ }
+
+ return size;
+}
+
+size_t PerfReader::GetNUMATopologyMetadataSize() const {
+ size_t size = sizeof(numa_topology_num_nodes_type);
+ for (size_t i = 0; i < numa_topology_.size(); ++i) {
+ const PerfNodeTopologyMetadata& node = numa_topology_[i];
+ size += sizeof(node.id);
+ size += sizeof(node.total_memory) + sizeof(node.free_memory);
+ size += sizeof(node.cpu_list.len) + node.cpu_list.len * sizeof(char);
+ }
+ return size;
+}
+
+bool PerfReader::NeedsNumberOfStringData(u32 type) const {
+ return type == HEADER_CMDLINE;
+}
+
+bool PerfReader::LocalizeMMapFilenames(
+ const std::map<string, string>& filename_map) {
+ // Search for mmap/mmap2 events for which the filename needs to be updated.
+ for (size_t i = 0; i < events_.size(); ++i) {
+ string filename;
+ size_t size_of_fixed_event_parts;
+ event_t* event = events_[i].get();
+ if (event->header.type == PERF_RECORD_MMAP) {
+ filename = string(event->mmap.filename);
+ size_of_fixed_event_parts =
+ sizeof(event->mmap) - sizeof(event->mmap.filename);
+ } else if (event->header.type == PERF_RECORD_MMAP2) {
+ filename = string(event->mmap2.filename);
+ size_of_fixed_event_parts =
+ sizeof(event->mmap2) - sizeof(event->mmap2.filename);
+ } else {
+ continue;
+ }
+
+ const auto it = filename_map.find(filename);
+ if (it == filename_map.end()) // not found
+ continue;
+
+ const string& new_filename = it->second;
+ size_t old_len = GetUint64AlignedStringLength(filename);
+ size_t new_len = GetUint64AlignedStringLength(new_filename);
+ size_t old_offset = GetPerfSampleDataOffset(*event);
+ size_t sample_size = event->header.size - old_offset;
+
+ int size_change = new_len - old_len;
+ size_t new_size = event->header.size + size_change;
+ size_t new_offset = old_offset + size_change;
+
+ if (size_change > 0) {
+ // Allocate memory for a new event.
+ event_t* old_event = event;
+ malloced_unique_ptr<event_t> new_event(CallocMemoryForEvent(new_size));
+
+ // Copy over everything except filename and sample info.
+ memcpy(new_event.get(), old_event, size_of_fixed_event_parts);
+
+ // Copy over the sample info to the correct location.
+ char* old_addr = reinterpret_cast<char*>(old_event);
+ char* new_addr = reinterpret_cast<char*>(new_event.get());
+ memcpy(new_addr + new_offset, old_addr + old_offset, sample_size);
+
+ events_[i] = std::move(new_event);
+ event = events_[i].get();
+ } else if (size_change < 0) {
+ // Move the perf sample data to its new location.
+ // Since source and dest could overlap, use memmove instead of memcpy.
+ char* start_addr = reinterpret_cast<char*>(event);
+ memmove(start_addr + new_offset, start_addr + old_offset, sample_size);
+ }
+
+ // Copy over the new filename and fix the size of the event.
+ char *event_filename = nullptr;
+ if (event->header.type == PERF_RECORD_MMAP) {
+ event_filename = event->mmap.filename;
+ } else if (event->header.type == PERF_RECORD_MMAP2) {
+ event_filename = event->mmap2.filename;
+ } else {
+ LOG(FATAL) << "Unexpected event type"; // Impossible
+ }
+ CHECK_GT(snprintf(event_filename, new_filename.size() + 1, "%s",
+ new_filename.c_str()),
+ 0);
+ event->header.size = new_size;
+ }
+
+ return true;
+}
+
+} // namespace quipper
diff --git a/perfprofd/quipper/perf_reader.h b/perfprofd/quipper/perf_reader.h
new file mode 100644
index 00000000..82163726
--- /dev/null
+++ b/perfprofd/quipper/perf_reader.h
@@ -0,0 +1,296 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMIUMOS_WIDE_PROFILING_PERF_READER_H_
+#define CHROMIUMOS_WIDE_PROFILING_PERF_READER_H_
+
+#include <stdint.h>
+
+#include <map>
+#include <set>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include "base/macros.h"
+
+#include "perf_internals.h"
+#include "quipper_string.h"
+#include "perf_utils.h"
+
+namespace quipper {
+
+struct PerfFileAttr {
+ struct perf_event_attr attr;
+ std::vector<u64> ids;
+};
+
+// Based on code in tools/perf/util/header.c, the metadata are of the following
+// formats:
+
+// Based on kernel/perf_internals.h
+const size_t kBuildIDArraySize = 20;
+const size_t kBuildIDStringLength = kBuildIDArraySize * 2;
+
+struct CStringWithLength {
+ u32 len;
+ string str;
+};
+
+struct PerfStringMetadata {
+ u32 type;
+ std::vector<CStringWithLength> data;
+};
+
+struct PerfUint32Metadata {
+ u32 type;
+ std::vector<uint32_t> data;
+};
+
+struct PerfUint64Metadata {
+ u32 type;
+ std::vector<uint64_t> data;
+};
+
+typedef u32 num_siblings_type;
+
+struct PerfCPUTopologyMetadata {
+ std::vector<CStringWithLength> core_siblings;
+ std::vector<CStringWithLength> thread_siblings;
+};
+
+struct PerfNodeTopologyMetadata {
+ u32 id;
+ u64 total_memory;
+ u64 free_memory;
+ CStringWithLength cpu_list;
+};
+
+struct BufferWithSize;
+struct ConstBufferWithSize;
+
+class PerfReader {
+ public:
+ PerfReader() : sample_type_(0),
+ read_format_(0),
+ is_cross_endian_(0) {}
+ ~PerfReader();
+
+ // Makes |build_id| fit the perf format, by either truncating it or adding
+ // zeros to the end so that it has length kBuildIDStringLength.
+ static void PerfizeBuildIDString(string* build_id);
+
+ // Changes |build_id| to the best guess of what the build id was before going
+ // through perf. Specifically, it keeps removing trailing sequences of four
+ // zero bytes (or eight '0' characters) until there are no more such
+ // sequences, or the build id would be empty if the process were repeated.
+ static void UnperfizeBuildIDString(string* build_id);
+
+ bool ReadFile(const string& filename);
+ bool ReadFromVector(const std::vector<char>& data);
+ bool ReadFromString(const string& str);
+ bool ReadFromPointer(const char* perf_data, size_t size);
+
+ // TODO(rohinmshah): GetSize should not use RegenerateHeader (so that it can
+ // be const). Ideally, RegenerateHeader would be deleted and instead of
+ // having out_header_ as an instance variable, it would be computed
+ // dynamically whenever needed.
+
+ // Returns the size in bytes that would be written by any of the methods that
+ // write the entire perf data file (WriteFile, WriteToPointer, etc).
+ size_t GetSize();
+
+ bool WriteFile(const string& filename);
+ bool WriteToVector(std::vector<char>* data);
+ bool WriteToString(string* str);
+ bool WriteToPointer(char* buffer, size_t size);
+
+ bool RegenerateHeader();
+
+ // Stores the mapping from filenames to build ids in build_id_events_.
+ // Returns true on success.
+ // Note: If |filenames_to_build_ids| contains a mapping for a filename for
+ // which there is already a build_id_event in build_id_events_, a duplicate
+ // build_id_event will be created, and the old build_id_event will NOT be
+ // deleted.
+ bool InjectBuildIDs(const std::map<string, string>& filenames_to_build_ids);
+
+ // Replaces existing filenames with filenames from |build_ids_to_filenames|
+ // by joining on build ids. If a build id in |build_ids_to_filenames| is not
+ // present in this parser, it is ignored.
+ bool Localize(const std::map<string, string>& build_ids_to_filenames);
+
+ // Same as Localize, but joins on filenames instead of build ids.
+ bool LocalizeUsingFilenames(const std::map<string, string>& filename_map);
+
+ // Stores a list of unique filenames found in MMAP/MMAP2 events into
+ // |filenames|. Any existing data in |filenames| will be lost.
+ void GetFilenames(std::vector<string>* filenames) const;
+ void GetFilenamesAsSet(std::set<string>* filenames) const;
+
+ // Uses build id events to populate |filenames_to_build_ids|.
+ // Any existing data in |filenames_to_build_ids| will be lost.
+ // Note: A filename returned by GetFilenames need not be present in this map,
+ // since there may be no build id event corresponding to the MMAP/MMAP2.
+ void GetFilenamesToBuildIDs(
+ std::map<string, string>* filenames_to_build_ids) const;
+
+ static bool IsSupportedEventType(uint32_t type);
+
+ // If a program using PerfReader calls events(), it could work with the
+ // resulting events by importing kernel/perf_internals.h. This would also
+ // apply to other forms of data (attributes, event types, build ids, etc.)
+ // However, there is no easy way to work with the sample info within events.
+ // The following two methods have been added for this purpose.
+
+ // Extracts from a perf event |event| info about the perf sample that
+ // contains the event. Stores info in |sample|.
+ bool ReadPerfSampleInfo(const event_t& event,
+ struct perf_sample* sample) const;
+ // Writes |sample| info back to a perf event |event|.
+ bool WritePerfSampleInfo(const perf_sample& sample,
+ event_t* event) const;
+
+ // Accessor funcs.
+ const std::vector<PerfFileAttr>& attrs() const {
+ return attrs_;
+ }
+
+ const std::vector<malloced_unique_ptr<event_t>>& events() const {
+ return events_;
+ }
+
+ const std::vector<perf_trace_event_type>& event_types() const {
+ return event_types_;
+ }
+
+ const std::vector<build_id_event*>& build_id_events() const {
+ return build_id_events_;
+ }
+
+ const std::vector<char>& tracing_data() const {
+ return tracing_data_;
+ }
+
+ protected:
+ bool ReadHeader(const ConstBufferWithSize& data);
+
+ bool ReadAttrs(const ConstBufferWithSize& data);
+ bool ReadAttr(const ConstBufferWithSize& data, size_t* offset);
+ bool ReadEventAttr(const ConstBufferWithSize& data, size_t* offset,
+ perf_event_attr* attr);
+ bool ReadUniqueIDs(const ConstBufferWithSize& data, size_t num_ids,
+ size_t* offset, std::vector<u64>* ids);
+
+ bool ReadEventTypes(const ConstBufferWithSize& data);
+ bool ReadEventType(const ConstBufferWithSize& data, size_t* offset);
+
+ bool ReadData(const ConstBufferWithSize& data);
+
+ // Reads metadata in normal mode.
+ bool ReadMetadata(const ConstBufferWithSize& data);
+ bool ReadTracingMetadata(const ConstBufferWithSize& data,
+ size_t offset, size_t size);
+ bool ReadBuildIDMetadata(const ConstBufferWithSize& data, u32 type,
+ size_t offset, size_t size);
+ bool ReadStringMetadata(const ConstBufferWithSize& data, u32 type,
+ size_t offset, size_t size);
+ bool ReadUint32Metadata(const ConstBufferWithSize& data, u32 type,
+ size_t offset, size_t size);
+ bool ReadUint64Metadata(const ConstBufferWithSize& data, u32 type,
+ size_t offset, size_t size);
+ bool ReadCPUTopologyMetadata(const ConstBufferWithSize& data, u32 type,
+ size_t offset, size_t size);
+ bool ReadNUMATopologyMetadata(const ConstBufferWithSize& data, u32 type,
+ size_t offset, size_t size);
+
+ // Read perf data from piped perf output data.
+ bool ReadPipedData(const ConstBufferWithSize& data);
+ bool ReadTracingMetadataEvent(const ConstBufferWithSize& data, size_t offset);
+
+ // Like WriteToPointer, but does not check if the buffer is large enough.
+ bool WriteToPointerWithoutCheckingSize(char* buffer, size_t size);
+
+ bool WriteHeader(const BufferWithSize& data) const;
+ bool WriteAttrs(const BufferWithSize& data) const;
+ bool WriteEventTypes(const BufferWithSize& data) const;
+ bool WriteData(const BufferWithSize& data) const;
+ bool WriteMetadata(const BufferWithSize& data) const;
+
+ // For writing the various types of metadata.
+ bool WriteBuildIDMetadata(u32 type, size_t* offset,
+ const BufferWithSize& data) const;
+ bool WriteStringMetadata(u32 type, size_t* offset,
+ const BufferWithSize& data) const;
+ bool WriteUint32Metadata(u32 type, size_t* offset,
+ const BufferWithSize& data) const;
+ bool WriteUint64Metadata(u32 type, size_t* offset,
+ const BufferWithSize& data) const;
+ bool WriteEventDescMetadata(u32 type, size_t* offset,
+ const BufferWithSize& data) const;
+ bool WriteCPUTopologyMetadata(u32 type, size_t* offset,
+ const BufferWithSize& data) const;
+ bool WriteNUMATopologyMetadata(u32 type, size_t* offset,
+ const BufferWithSize& data) const;
+
+ // For reading event blocks within piped perf data.
+ bool ReadAttrEventBlock(const ConstBufferWithSize& data, size_t offset,
+ size_t size);
+ bool ReadPerfEventBlock(const event_t& event);
+
+ // Returns the number of types of metadata stored.
+ size_t GetNumMetadata() const;
+
+ // For computing the sizes of the various types of metadata.
+ size_t GetBuildIDMetadataSize() const;
+ size_t GetStringMetadataSize() const;
+ size_t GetUint32MetadataSize() const;
+ size_t GetUint64MetadataSize() const;
+ size_t GetEventDescMetadataSize() const;
+ size_t GetCPUTopologyMetadataSize() const;
+ size_t GetNUMATopologyMetadataSize() const;
+
+ // Returns true if we should write the number of strings for the string
+ // metadata of type |type|.
+ bool NeedsNumberOfStringData(u32 type) const;
+
+ // Replaces existing filenames in MMAP/MMAP2 events based on |filename_map|.
+ // This method does not change |build_id_events_|.
+ bool LocalizeMMapFilenames(const std::map<string, string>& filename_map);
+
+ std::vector<PerfFileAttr> attrs_;
+ std::vector<perf_trace_event_type> event_types_;
+ std::vector<malloced_unique_ptr<event_t>> events_;
+ std::vector<build_id_event*> build_id_events_;
+ std::vector<PerfStringMetadata> string_metadata_;
+ std::vector<PerfUint32Metadata> uint32_metadata_;
+ std::vector<PerfUint64Metadata> uint64_metadata_;
+ PerfCPUTopologyMetadata cpu_topology_;
+ std::vector<PerfNodeTopologyMetadata> numa_topology_;
+ std::vector<char> tracing_data_;
+ uint64_t sample_type_;
+ uint64_t read_format_;
+ uint64_t metadata_mask_;
+
+ // Indicates that the perf data being read is from machine with a different
+ // endianness than the current machine.
+ bool is_cross_endian_;
+
+ private:
+ u32 ReadPerfEventAttrSize(const ConstBufferWithSize& data,
+ size_t attr_offset);
+
+ // The file header is either a normal header or a piped header.
+ union {
+ struct perf_file_header header_;
+ struct perf_pipe_file_header piped_header_;
+ };
+ struct perf_file_header out_header_;
+
+ DISALLOW_COPY_AND_ASSIGN(PerfReader);
+};
+
+} // namespace quipper
+
+#endif // CHROMIUMOS_WIDE_PROFILING_PERF_READER_H_
diff --git a/perfprofd/quipper/perf_utils.cc b/perfprofd/quipper/perf_utils.cc
new file mode 100644
index 00000000..02fa9e06
--- /dev/null
+++ b/perfprofd/quipper/perf_utils.cc
@@ -0,0 +1,180 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#define LOG_TAG "perf_reader"
+
+#include "perf_utils.h"
+
+#include <sys/stat.h>
+
+#include <cctype>
+#include <cstddef>
+#include <cstdlib>
+#include <cstring>
+#include <fstream> // NOLINT(readability/streams)
+#include <iomanip>
+#include <sstream>
+
+#include "base/logging.h"
+#include "base/macros.h"
+
+namespace {
+
+// Number of hex digits in a byte.
+const int kNumHexDigitsInByte = 2;
+
+} // namespace
+
+namespace quipper {
+
+event_t* CallocMemoryForEvent(size_t size) {
+ event_t* event = reinterpret_cast<event_t*>(calloc(1, size));
+ CHECK(event);
+ return event;
+}
+
+build_id_event* CallocMemoryForBuildID(size_t size) {
+ build_id_event* event = reinterpret_cast<build_id_event*>(calloc(1, size));
+ CHECK(event);
+ return event;
+}
+
+string HexToString(const u8* array, size_t length) {
+ // Convert the bytes to hex digits one at a time.
+ // There will be kNumHexDigitsInByte hex digits, and 1 char for NUL.
+ char buffer[kNumHexDigitsInByte + 1];
+ string result = "";
+ for (size_t i = 0; i < length; ++i) {
+ snprintf(buffer, sizeof(buffer), "%02x", array[i]);
+ result += buffer;
+ }
+ return result;
+}
+
+bool StringToHex(const string& str, u8* array, size_t length) {
+ const int kHexRadix = 16;
+ char* err;
+ // Loop through kNumHexDigitsInByte characters at a time (to get one byte)
+ // Stop when there are no more characters, or the array has been filled.
+ for (size_t i = 0;
+ (i + 1) * kNumHexDigitsInByte <= str.size() && i < length;
+ ++i) {
+ string one_byte = str.substr(i * kNumHexDigitsInByte, kNumHexDigitsInByte);
+ array[i] = strtol(one_byte.c_str(), &err, kHexRadix);
+ if (*err)
+ return false;
+ }
+ return true;
+}
+
+uint64_t AlignSize(uint64_t size, uint32_t align_size) {
+ return ((size + align_size - 1) / align_size) * align_size;
+}
+
+// In perf data, strings are packed into the smallest number of 8-byte blocks
+// possible, including the null terminator.
+// e.g.
+// "0123" -> 5 bytes -> packed into 8 bytes
+// "0123456" -> 8 bytes -> packed into 8 bytes
+// "01234567" -> 9 bytes -> packed into 16 bytes
+// "0123456789abcd" -> 15 bytes -> packed into 16 bytes
+// "0123456789abcde" -> 16 bytes -> packed into 16 bytes
+// "0123456789abcdef" -> 17 bytes -> packed into 24 bytes
+//
+// Returns the size of the 8-byte-aligned memory for storing |string|.
+size_t GetUint64AlignedStringLength(const string& str) {
+ return AlignSize(str.size() + 1, sizeof(uint64_t));
+}
+
+uint64_t GetSampleFieldsForEventType(uint32_t event_type,
+ uint64_t sample_type) {
+ uint64_t mask = kuint64max;
+ switch (event_type) {
+ case PERF_RECORD_MMAP:
+ case PERF_RECORD_LOST:
+ case PERF_RECORD_COMM:
+ case PERF_RECORD_EXIT:
+ case PERF_RECORD_THROTTLE:
+ case PERF_RECORD_UNTHROTTLE:
+ case PERF_RECORD_FORK:
+ case PERF_RECORD_READ:
+ case PERF_RECORD_MMAP2:
+ // See perf_event.h "struct" sample_id and sample_id_all.
+ mask = PERF_SAMPLE_TID | PERF_SAMPLE_TIME | PERF_SAMPLE_ID |
+ PERF_SAMPLE_STREAM_ID | PERF_SAMPLE_CPU | PERF_SAMPLE_IDENTIFIER;
+ break;
+ case PERF_RECORD_SAMPLE:
+ break;
+ default:
+ LOG(FATAL) << "Unknown event type " << event_type;
+ }
+ return sample_type & mask;
+}
+
+uint64_t GetPerfSampleDataOffset(const event_t& event) {
+ uint64_t offset = kuint64max;
+ switch (event.header.type) {
+ case PERF_RECORD_SAMPLE:
+ offset = offsetof(event_t, sample.array);
+ break;
+ case PERF_RECORD_MMAP:
+ offset = sizeof(event.mmap) - sizeof(event.mmap.filename) +
+ GetUint64AlignedStringLength(event.mmap.filename);
+ break;
+ case PERF_RECORD_FORK:
+ case PERF_RECORD_EXIT:
+ offset = sizeof(event.fork);
+ break;
+ case PERF_RECORD_COMM:
+ offset = sizeof(event.comm) - sizeof(event.comm.comm) +
+ GetUint64AlignedStringLength(event.comm.comm);
+ break;
+ case PERF_RECORD_LOST:
+ offset = sizeof(event.lost);
+ break;
+ case PERF_RECORD_READ:
+ offset = sizeof(event.read);
+ break;
+ case PERF_RECORD_MMAP2:
+ offset = sizeof(event.mmap2) - sizeof(event.mmap2.filename) +
+ GetUint64AlignedStringLength(event.mmap2.filename);
+ break;
+ default:
+ LOG(FATAL) << "Unknown/unsupported event type " << event.header.type;
+ break;
+ }
+ // Make sure the offset was valid
+ CHECK_NE(offset, kuint64max);
+ CHECK_EQ(offset % sizeof(uint64_t), 0U);
+ return offset;
+}
+
+bool ReadFileToData(const string& filename, std::vector<char>* data) {
+ std::ifstream in(filename.c_str(), std::ios::binary);
+ if (!in.good()) {
+ LOG(ERROR) << "Failed to open file " << filename;
+ return false;
+ }
+ in.seekg(0, in.end);
+ size_t length = in.tellg();
+ in.seekg(0, in.beg);
+ data->resize(length);
+
+ in.read(&(*data)[0], length);
+
+ if (!in.good()) {
+ LOG(ERROR) << "Error reading from file " << filename;
+ return false;
+ }
+ return true;
+}
+
+bool WriteDataToFile(const std::vector<char>& data, const string& filename) {
+ std::ofstream out(filename.c_str(), std::ios::binary);
+ out.seekp(0, std::ios::beg);
+ out.write(&data[0], data.size());
+ return out.good();
+}
+
+} // namespace quipper
diff --git a/perfprofd/quipper/perf_utils.h b/perfprofd/quipper/perf_utils.h
new file mode 100644
index 00000000..66f1d9ec
--- /dev/null
+++ b/perfprofd/quipper/perf_utils.h
@@ -0,0 +1,112 @@
+// Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMIUMOS_WIDE_PROFILING_UTILS_H_
+#define CHROMIUMOS_WIDE_PROFILING_UTILS_H_
+
+#include <stdint.h>
+#include <stdlib.h> // for free()
+
+#include <memory>
+#include <string>
+#include <vector>
+
+#include "base/logging.h"
+
+#include "perf_internals.h"
+#include "quipper_string.h"
+
+namespace quipper {
+
+struct FreeDeleter {
+ inline void operator()(void* pointer) {
+ free(pointer);
+ }
+};
+
+template <typename T>
+using malloced_unique_ptr = std::unique_ptr<T, FreeDeleter>;
+
+// Given a valid open file handle |fp|, returns the size of the file.
+int64_t GetFileSizeFromHandle(FILE* fp);
+
+event_t* CallocMemoryForEvent(size_t size);
+event_t* ReallocMemoryForEvent(event_t* event, size_t new_size);
+
+build_id_event* CallocMemoryForBuildID(size_t size);
+
+bool FileToBuffer(const string& filename, std::vector<char>* contents);
+
+template <typename CharContainer>
+bool BufferToFile(const string& filename, const CharContainer& contents) {
+ FILE* fp = fopen(filename.c_str(), "wb");
+ if (!fp)
+ return false;
+ // Do not write anything if |contents| contains nothing. fopen will create
+ // an empty file.
+ if (!contents.empty()) {
+ CHECK_EQ(fwrite(contents.data(),
+ sizeof(typename CharContainer::value_type),
+ contents.size(),
+ fp),
+ contents.size());
+ }
+ fclose(fp);
+ return true;
+}
+
+uint64_t Md5Prefix(const string& input);
+uint64_t Md5Prefix(const std::vector<char>& input);
+
+// Returns a string that represents |array| in hexadecimal.
+string HexToString(const u8* array, size_t length);
+
+// Converts |str| to a hexadecimal number, stored in |array|. Returns true on
+// success. Only stores up to |length| bytes - if there are more characters in
+// the string, they are ignored (but the function may still return true).
+bool StringToHex(const string& str, u8* array, size_t length);
+
+// Adjust |size| to blocks of |align_size|. i.e. returns the smallest multiple
+// of |align_size| that can fit |size|.
+uint64_t AlignSize(uint64_t size, uint32_t align_size);
+
+// Given a general perf sample format |sample_type|, return the fields of that
+// format that are present in a sample for an event of type |event_type|.
+//
+// e.g. FORK and EXIT events have the fields {time, pid/tid, cpu, id}.
+// Given a sample type with fields {ip, time, pid/tid, and period}, return
+// the intersection of these two field sets: {time, pid/tid}.
+//
+// All field formats are bitfields, as defined by enum perf_event_sample_format
+// in kernel/perf_event.h.
+uint64_t GetSampleFieldsForEventType(uint32_t event_type, uint64_t sample_type);
+
+// Returns the offset in bytes within a perf event structure at which the raw
+// perf sample data is located.
+uint64_t GetPerfSampleDataOffset(const event_t& event);
+
+// Returns the size of the 8-byte-aligned memory for storing |string|.
+size_t GetUint64AlignedStringLength(const string& str);
+
+// Returns true iff the file exists.
+bool FileExists(const string& filename);
+
+// Reads the contents of a file into |data|. Returns true on success, false if
+// it fails.
+bool ReadFileToData(const string& filename, std::vector<char>* data);
+
+// Writes contents of |data| to a file with name |filename|, overwriting any
+// existing file. Returns true on success, false if it fails.
+bool WriteDataToFile(const std::vector<char>& data, const string& filename);
+
+// Executes |command| and stores stdout output in |output|. Returns true on
+// success, false otherwise.
+bool RunCommandAndGetStdout(const string& command, std::vector<char>* output);
+
+// Trim leading and trailing whitespace from |str|.
+void TrimWhitespace(string* str);
+
+} // namespace quipper
+
+#endif // CHROMIUMOS_WIDE_PROFILING_UTILS_H_
diff --git a/perfprofd/quipper/quipper_string.h b/perfprofd/quipper/quipper_string.h
new file mode 100644
index 00000000..7b0ad1e8
--- /dev/null
+++ b/perfprofd/quipper/quipper_string.h
@@ -0,0 +1,13 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUIPPER_STRING_
+#define QUIPPER_STRING_
+
+#ifndef HAS_GLOBAL_STRING
+using std::string;
+using std::stringstream;
+#endif
+
+#endif // QUIPPER_STRING_
diff --git a/perfprofd/quipper/quipper_test.h b/perfprofd/quipper/quipper_test.h
new file mode 100644
index 00000000..85e8aea1
--- /dev/null
+++ b/perfprofd/quipper/quipper_test.h
@@ -0,0 +1,10 @@
+// Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef QUIPPER_TEST_H_
+#define QUIPPER_TEST_H_
+
+#include <gtest/gtest.h>
+
+#endif // QUIPPER_TEST_H_
diff --git a/perfprofd/tests/Android.mk b/perfprofd/tests/Android.mk
new file mode 100644
index 00000000..c8347a11
--- /dev/null
+++ b/perfprofd/tests/Android.mk
@@ -0,0 +1,49 @@
+# Build the unit tests.
+LOCAL_PATH := $(call my-dir)
+
+perfprofd_test_cppflags := -Wall -Wno-sign-compare -Wno-unused-parameter -Werror -std=gnu++11
+
+#
+# Static library with mockup utilities layer (called by unit test).
+#
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_CPP_EXTENSION := cc
+LOCAL_CXX_STL := libc++
+LOCAL_C_INCLUDES += system/extras/perfprofd
+LOCAL_MODULE := libperfprofdmockutils
+LOCAL_CPPFLAGS += $(perfprofd_test_cppflags)
+LOCAL_SRC_FILES := perfprofdmockutils.cc
+include $(BUILD_STATIC_LIBRARY)
+
+#
+# Canned perf.data files needed by unit test.
+#
+include $(CLEAR_VARS)
+LOCAL_MODULE := canned.perf.data
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := DATA
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/nativetest/perfprofd_test
+LOCAL_SRC_FILES := canned.perf.data
+include $(BUILD_PREBUILT)
+
+#
+# Unit test for perfprofd
+#
+include $(CLEAR_VARS)
+LOCAL_CLANG := true
+LOCAL_CPP_EXTENSION := cc
+LOCAL_CXX_STL := libc++
+LOCAL_STATIC_LIBRARIES := \
+ libperfprofdcore \
+ libperfprofdmockutils
+LOCAL_SHARED_LIBRARIES := libprotobuf-cpp-full
+LOCAL_C_INCLUDES += system/extras/perfprofd external/protobuf/src
+LOCAL_SRC_FILES := perfprofd_test.cc
+LOCAL_CPPFLAGS += $(perfprofd_test_cppflags)
+LOCAL_SHARED_LIBRARIES += libcutils
+LOCAL_MODULE := perfprofd_test
+include $(BUILD_NATIVE_TEST)
+
+# Clean temp vars
+perfprofd_test_cppflags :=
diff --git a/perfprofd/tests/README.txt b/perfprofd/tests/README.txt
new file mode 100644
index 00000000..4d8db99e
--- /dev/null
+++ b/perfprofd/tests/README.txt
@@ -0,0 +1,58 @@
+Native tests for 'perfprofd'. Please run with 'runtest perfprofd'
+(a.k.a. "$ANDROID_BUILD_TOP"/development/testrunner/runtest.py).
+
+Notes:
+
+1. One of the testpoints in this test suite performs a live 'perf'
+run on the device; before invoking the test be sure that 'perf'
+has been built and installed on the device in /system/bin/perf
+
+2. The daemon under test, perfprofd, is broken into a main function, a
+"core" library, and a "utils library. Picture:
+
+ +-----------+ perfprofdmain.o
+ | perfprofd |
+ | main() | 1-liner; calls perfprofd_main()
+ +-----------+
+ |
+ v
+ +-----------+ perfprofdcore.a
+ | perfprofd |
+ | core | most of the interesting code is here;
+ | | calls into utils library when for
+ +-----------+ operations such as sleep, log, etc
+ |
+ v
+ +-----------+ perfprofdutils.a
+ | perfprofd |
+ | utils | real implementations of perfprofd_sleep,
+ | | perfprofd_log_* etc
+ +-----------+
+
+Because the daemon tends to spend a lot of time sleeping/waiting,
+it is impractical to try to test it directly. Instead we insert a
+mock utilities layer and then have a test driver that invokes the
+daemon main function. Picture for perfprofd_test:
+
+ +----------------+ perfprofd_test.cc
+ | perfprofd_test |
+ | | makes calls into perfprofd_main(),
+ +----------------+ then verifies behavior
+ |
+ v
+ +-----------+ perfprofdcore.a
+ | perfprofd |
+ | core | same as above
+ +-----------+
+ |
+ v
+ +-----------+ perfprofdmockutils.a
+ | perfprofd |
+ | mockutils | mock implementations of perfprofd_sleep,
+ | | perfprofd_log_* etc
+ +-----------+
+
+The mockup versions of perfprofd_sleep() and perfprofd_log_* do
+simply log the fact that they are called; the test driver can
+then examine the log to make sure that the daemon is doing
+what it is supposed to be doing.
diff --git a/perfprofd/tests/canned.perf.data b/perfprofd/tests/canned.perf.data
new file mode 100644
index 00000000..e6510d2a
--- /dev/null
+++ b/perfprofd/tests/canned.perf.data
Binary files differ
diff --git a/perfprofd/tests/perfprofd_test.cc b/perfprofd/tests/perfprofd_test.cc
new file mode 100644
index 00000000..94c2814c
--- /dev/null
+++ b/perfprofd/tests/perfprofd_test.cc
@@ -0,0 +1,639 @@
+/*
+ * 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 <algorithm>
+#include <cctype>
+#include <string>
+#include <regex>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "perfprofdcore.h"
+#include "perfprofdutils.h"
+#include "perfprofdmockutils.h"
+
+#include "perf_profile.pb.h"
+#include "google/protobuf/text_format.h"
+
+//
+// Set to argv[0] on startup
+//
+static const char *executable_path;
+
+//
+// test_dir is the directory containing the test executable and
+// any files associated with the test (will be created by the harness).
+//
+// dest_dir is a subdirectory of test_dir that we'll create on the fly
+// at the start of each testpoint (into which new files can be written),
+// then delete at end of testpoint.
+//
+static std::string test_dir;
+static std::string dest_dir;
+
+// Path to perf executable on device
+#define PERFPATH "/system/bin/perf"
+
+// Temporary config file that we will emit for the daemon to read
+#define CONFIGFILE "perfprofd.conf"
+
+static std::string encoded_file_path()
+{
+ std::string path(dest_dir);
+ path += "/perf.data.encoded";
+ return path;
+}
+
+class PerfProfdTest : public testing::Test {
+ protected:
+ virtual void SetUp() {
+ mock_perfprofdutils_init();
+ create_dest_dir();
+ }
+
+ virtual void TearDown() {
+ mock_perfprofdutils_finish();
+ remove_dest_dir();
+ }
+
+ private:
+
+ void create_dest_dir() {
+ setup_dirs();
+ ASSERT_FALSE(dest_dir == "");
+ std::string cmd("mkdir -p ");
+ cmd += dest_dir;
+ system(cmd.c_str());
+ }
+
+ void remove_dest_dir() {
+ setup_dirs();
+ ASSERT_FALSE(dest_dir == "");
+ std::string cmd("rm -rf ");
+ cmd += dest_dir;
+ system(cmd.c_str());
+ }
+
+ void setup_dirs()
+ {
+ if (test_dir == "") {
+ ASSERT_TRUE(executable_path != nullptr);
+ std::string s(executable_path);
+ auto found = s.find_last_of("/");
+ test_dir = s.substr(0,found);
+ dest_dir = test_dir;
+ dest_dir += "/tmp";
+ }
+ }
+
+};
+
+static bool bothWhiteSpace(char lhs, char rhs)
+{
+ return (std::isspace(lhs) && std::isspace(rhs));
+}
+
+//
+// Squeeze out repeated whitespace from expected/actual logs.
+//
+static std::string squeezeWhite(const std::string &str,
+ const char *tag,
+ bool dump=false)
+{
+ if (dump) { fprintf(stderr, "raw %s is %s\n", tag, str.c_str()); }
+ std::string result(str);
+ std::replace( result.begin(), result.end(), '\n', ' ');
+ auto new_end = std::unique(result.begin(), result.end(), bothWhiteSpace);
+ result.erase(new_end, result.end());
+ while (result.begin() != result.end() && std::isspace(*result.rbegin())) {
+ result.pop_back();
+ }
+ if (dump) { fprintf(stderr, "squeezed %s is %s\n", tag, result.c_str()); }
+ return result;
+}
+
+///
+/// Helper class to kick off a run of the perfprofd daemon with a specific
+/// config file.
+///
+class PerfProfdRunner {
+ public:
+ PerfProfdRunner()
+ : config_path_(test_dir)
+ , aux_config_path_(dest_dir)
+ {
+ config_path_ += "/" CONFIGFILE;
+ aux_config_path_ += "/" CONFIGFILE;
+ }
+
+ ~PerfProfdRunner()
+ {
+ }
+
+ void addToConfig(const std::string &line)
+ {
+ config_text_ += line;
+ config_text_ += "\n";
+ }
+
+ void addToAuxConfig(const std::string &line)
+ {
+ aux_config_text_ += line;
+ aux_config_text_ += "\n";
+ }
+
+ void remove_semaphore_file()
+ {
+ std::string semaphore(dest_dir);
+ semaphore += "/" SEMAPHORE_FILENAME;
+ unlink(semaphore.c_str());
+ }
+
+ void create_semaphore_file()
+ {
+ std::string semaphore(dest_dir);
+ semaphore += "/" SEMAPHORE_FILENAME;
+ close(open(semaphore.c_str(), O_WRONLY|O_CREAT));
+ }
+
+ int invoke()
+ {
+ static const char *argv[3] = { "perfprofd", "-c", "" };
+ argv[2] = config_path_.c_str();
+
+ writeConfigFile(config_path_, config_text_);
+ if (aux_config_text_.length()) {
+ writeConfigFile(aux_config_path_, aux_config_text_);
+ }
+
+ // execute daemon main
+ return perfprofd_main(3, (char **) argv);
+ }
+
+ private:
+ std::string config_path_;
+ std::string config_text_;
+ std::string aux_config_path_;
+ std::string aux_config_text_;
+
+ void writeConfigFile(const std::string &config_path,
+ const std::string &config_text)
+ {
+ FILE *fp = fopen(config_path.c_str(), "w");
+ ASSERT_TRUE(fp != nullptr);
+ fprintf(fp, "%s\n", config_text.c_str());
+ fclose(fp);
+ }
+};
+
+//......................................................................
+
+static void readEncodedProfile(const char *testpoint,
+ wireless_android_play_playlog::AndroidPerfProfile &encodedProfile,
+ bool debugDump=false)
+{
+ struct stat statb;
+ int perf_data_stat_result = stat(encoded_file_path().c_str(), &statb);
+ ASSERT_NE(-1, perf_data_stat_result);
+
+ // read
+ std::string encoded;
+ encoded.resize(statb.st_size);
+ FILE *ifp = fopen(encoded_file_path().c_str(), "r");
+ ASSERT_NE(nullptr, ifp);
+ size_t items_read = fread((void*) encoded.data(), statb.st_size, 1, ifp);
+ ASSERT_EQ(1, items_read);
+ fclose(ifp);
+
+ // decode
+ encodedProfile.ParseFromString(encoded);
+
+ if (debugDump) {
+ std::string textdump;
+ ::google::protobuf::TextFormat::PrintToString(encodedProfile, &textdump);
+ std::string dfp(dest_dir); dfp += "/"; dfp += testpoint; dfp += ".dump_encoded.txt";
+ FILE *ofp = fopen(dfp.c_str(), "w");
+ if (ofp) {
+ fwrite(textdump.c_str(), textdump.size(), 1, ofp);
+ fclose(ofp);
+ }
+ }
+}
+
+#define RAW_RESULT(x) #x
+
+//
+// Check to see if the log messages emitted by the daemon
+// match the expected result. By default we use a partial
+// match, e.g. if we see the expected excerpt anywhere in the
+// result, it's a match (for exact match, set exact to true)
+//
+static void compareLogMessages(const std::string &actual,
+ const std::string &expected,
+ const char *testpoint,
+ bool exactMatch=false)
+{
+ std::string sqexp = squeezeWhite(expected, "expected");
+ std::string sqact = squeezeWhite(actual, "actual");
+ if (exactMatch) {
+ EXPECT_STREQ(sqexp.c_str(), sqact.c_str());
+ } else {
+ std::size_t foundpos = sqact.find(sqexp);
+ bool wasFound = true;
+ if (foundpos == std::string::npos) {
+ std::cerr << testpoint << ": expected result not found\n";
+ std::cerr << " Actual: \"" << sqact << "\"\n";
+ std::cerr << " Expected: \"" << sqexp << "\"\n";
+ wasFound = false;
+ }
+ EXPECT_TRUE(wasFound);
+ }
+}
+
+TEST_F(PerfProfdTest, MissingGMS)
+{
+ //
+ // AWP requires cooperation between the daemon and the GMS core
+ // piece. If we're running on a device that has an old or damaged
+ // version of GMS core, then the directory we're interested in may
+ // not be there. This test insures that the daemon does the right
+ // thing in this case.
+ //
+ PerfProfdRunner runner;
+ runner.addToConfig("only_debug_build=0");
+ runner.addToConfig("trace_config_read=1");
+ runner.addToConfig("destination_directory=/does/not/exist");
+ runner.addToConfig("main_loop_iterations=1");
+ runner.addToConfig("use_fixed_seed=1");
+ runner.addToConfig("collection_interval=100");
+
+ // Kick off daemon
+ int daemon_main_return_code = runner.invoke();
+
+ // Check return code from daemon
+ EXPECT_EQ(0, daemon_main_return_code);
+
+ // Verify log contents
+ const std::string expected = RAW_RESULT(
+ I: starting Android Wide Profiling daemon
+ I: config file path set to /data/nativetest/perfprofd_test/perfprofd.conf
+ I: option destination_directory set to /does/not/exist
+ I: option main_loop_iterations set to 1
+ I: option use_fixed_seed set to 1
+ I: option collection_interval set to 100
+ I: random seed set to 1
+ I: sleep 90 seconds
+ W: unable to open destination directory /does/not/exist: (No such file or directory)
+ I: profile collection skipped (missing destination directory)
+ I: sleep 10 seconds
+ I: finishing Android Wide Profiling daemon
+ );\
+
+ // check to make sure entire log matches
+ bool compareEntireLog = true;
+ compareLogMessages(mock_perfprofdutils_getlogged(),
+ expected, "MissingGMS", compareEntireLog);
+}
+
+TEST_F(PerfProfdTest, MissingOptInSemaphoreFile)
+{
+ //
+ // Android device owners must opt in to "collect and report usage
+ // data" in order for us to be able to collect profiles. The opt-in
+ // check is performed in the GMS core component; if the check
+ // passes, then it creates a semaphore file for the daemon to pick
+ // up on.
+ //
+ PerfProfdRunner runner;
+ runner.addToConfig("only_debug_build=0");
+ std::string ddparam("destination_directory="); ddparam += dest_dir;
+ runner.addToConfig(ddparam);
+ runner.addToConfig("main_loop_iterations=1");
+ runner.addToConfig("use_fixed_seed=1");
+ runner.addToConfig("collection_interval=100");
+
+ runner.remove_semaphore_file();
+
+ // Kick off daemon
+ int daemon_main_return_code = runner.invoke();
+
+ // Check return code from daemon
+ EXPECT_EQ(0, daemon_main_return_code);
+
+ // Verify log contents
+ const std::string expected = RAW_RESULT(
+ I: profile collection skipped (missing semaphore file)
+ );
+ // check to make sure log excerpt matches
+ compareLogMessages(mock_perfprofdutils_getlogged(),
+ expected, "MissingOptInSemaphoreFile");
+}
+
+TEST_F(PerfProfdTest, MissingPerfExecutable)
+{
+ //
+ // AWP currently relies on the 'perf' tool to collect profiles (although
+ // this may conceivably change in the future). This test checks to make
+ // sure that if 'perf' is not present we bail out from collecting profiles.
+ //
+ PerfProfdRunner runner;
+ runner.addToConfig("only_debug_build=0");
+ runner.addToConfig("trace_config_read=1");
+ std::string ddparam("destination_directory="); ddparam += dest_dir;
+ runner.addToConfig(ddparam);
+ runner.addToConfig("main_loop_iterations=1");
+ runner.addToConfig("use_fixed_seed=1");
+ runner.addToConfig("collection_interval=100");
+ runner.addToConfig("perf_path=/does/not/exist");
+
+ // Create semaphore file
+ runner.create_semaphore_file();
+
+ // Kick off daemon
+ int daemon_main_return_code = runner.invoke();
+
+ // Check return code from daemon
+ EXPECT_EQ(0, daemon_main_return_code);
+
+ // expected log contents
+ const std::string expected = RAW_RESULT(
+ I: profile collection skipped (missing 'perf' executable)
+ );
+ // check to make sure log excerpt matches
+ compareLogMessages(mock_perfprofdutils_getlogged(),
+ expected, "MissingPerfExecutable");
+}
+
+TEST_F(PerfProfdTest, BadPerfRun)
+{
+ //
+ // The linux 'perf' tool tends to be tightly coupled with a
+ // specific kernel version -- if things are out of sync perf could
+ // easily fail or crash. This test makes sure that we detect such a
+ // case and log the error.
+ //
+ PerfProfdRunner runner;
+ runner.addToConfig("only_debug_build=0");
+ std::string ddparam("destination_directory="); ddparam += dest_dir;
+ runner.addToConfig(ddparam);
+ runner.addToConfig("main_loop_iterations=1");
+ runner.addToConfig("use_fixed_seed=1");
+ runner.addToConfig("collection_interval=100");
+ runner.addToConfig("perf_path=/system/bin/false");
+
+ // Create semaphore file
+ runner.create_semaphore_file();
+
+ // Kick off daemon
+ int daemon_main_return_code = runner.invoke();
+
+ // Check return code from daemon
+ EXPECT_EQ(0, daemon_main_return_code);
+
+ // Verify log contents
+ const std::string expected = RAW_RESULT(
+ I: profile collection failed (perf record returned bad exit status)
+ );
+
+ // check to make sure log excerpt matches
+ compareLogMessages(mock_perfprofdutils_getlogged(),
+ expected, "BadPerfRun");
+}
+
+TEST_F(PerfProfdTest, ConfigFileParsing)
+{
+ //
+ // Gracefully handly malformed items in the config file
+ //
+ PerfProfdRunner runner;
+ runner.addToConfig("only_debug_build=0");
+ runner.addToConfig("main_loop_iterations=1");
+ runner.addToConfig("collection_interval=100");
+ runner.addToConfig("use_fixed_seed=1");
+ runner.addToConfig("destination_directory=/does/not/exist");
+
+ // assorted bad syntax
+ runner.addToConfig("collection_interval=0");
+ runner.addToConfig("collection_interval=-1");
+ runner.addToConfig("collection_interval=2");
+ runner.addToConfig("nonexistent_key=something");
+ runner.addToConfig("no_equals_stmt");
+
+ // Kick off daemon
+ int daemon_main_return_code = runner.invoke();
+
+ // Check return code from daemon
+ EXPECT_EQ(0, daemon_main_return_code);
+
+ // Verify log contents
+ const std::string expected = RAW_RESULT(
+ W: line 6: specified value 0 for 'collection_interval' outside permitted range [100 4294967295] (ignored)
+ W: line 7: malformed unsigned value (ignored)
+ W: line 8: specified value 2 for 'collection_interval' outside permitted range [100 4294967295] (ignored)
+ W: line 9: unknown option 'nonexistent_key' ignored
+ W: line 10: line malformed (no '=' found)
+ );
+
+ // check to make sure log excerpt matches
+ compareLogMessages(mock_perfprofdutils_getlogged(),
+ expected, "ConfigFileParsing");
+}
+
+TEST_F(PerfProfdTest, AuxiliaryConfigFile)
+{
+ //
+ // We want to be able to tweak profile collection parameters (sample
+ // duration, etc) using changes to gservices. To carry this out, the
+ // GMS core upload service writes out an perfprofd.conf config file when
+ // it starts up. This test verifies that we can read this file.
+ //
+
+ // Minimal settings in main config file
+ PerfProfdRunner runner;
+ runner.addToConfig("only_debug_build=0");
+ runner.addToConfig("trace_config_read=1");
+ runner.addToConfig("use_fixed_seed=1");
+ std::string ddparam("destination_directory="); ddparam += dest_dir;
+ runner.addToConfig(ddparam);
+
+ // Remaining settings in aux config file
+ runner.addToAuxConfig("main_loop_iterations=1");
+ runner.addToAuxConfig("collection_interval=100");
+ runner.addToAuxConfig("perf_path=/system/bin/true");
+ runner.addToAuxConfig("stack_profile=1");
+ runner.addToAuxConfig("sampling_period=9999");
+ runner.addToAuxConfig("sample_duration=333");
+
+ runner.remove_semaphore_file();
+
+ // Kick off daemon
+ int daemon_main_return_code = runner.invoke();
+
+ // Check return code from daemon
+ EXPECT_EQ(0, daemon_main_return_code);
+
+ // Verify log contents
+ const std::string expected = RAW_RESULT(
+ I: reading auxiliary config file /data/nativetest/perfprofd_test/tmp/perfprofd.conf
+ I: option main_loop_iterations set to 1
+ I: option collection_interval set to 100
+ I: option perf_path set to /system/bin/true
+ I: option stack_profile set to 1
+ I: option sampling_period set to 9999
+ I: option sample_duration set to 333
+ I: sleep 90 seconds
+ I: reading auxiliary config file /data/nativetest/perfprofd_test/tmp/perfprofd.conf
+ I: option main_loop_iterations set to 1
+ );
+
+ // check to make sure log excerpt matches
+ compareLogMessages(mock_perfprofdutils_getlogged(),
+ expected, "AuxiliaryConfigFile");
+}
+
+TEST_F(PerfProfdTest, BasicRunWithCannedPerf)
+{
+ //
+ // Verify the portion of the daemon that reads and encodes
+ // perf.data files. Here we run the encoder on a canned perf.data
+ // file and verify that the resulting protobuf contains what
+ // we think it should contain.
+ //
+ std::string input_perf_data(test_dir);
+ input_perf_data += "/canned.perf.data";
+
+ // Kick off encoder and check return code
+ PROFILE_RESULT result =
+ encode_to_proto(input_perf_data, encoded_file_path());
+ EXPECT_EQ(OK_PROFILE_COLLECTION, result);
+
+ // Read and decode the resulting perf.data.encoded file
+ wireless_android_play_playlog::AndroidPerfProfile encodedProfile;
+ readEncodedProfile("BasicRunWithCannedPerf",
+ encodedProfile);
+
+ // Expect 29 load modules
+ EXPECT_EQ(29, encodedProfile.programs_size());
+
+ // Check a couple of load modules
+ { const auto &lm0 = encodedProfile.load_modules(0);
+ std::string act_lm0;
+ ::google::protobuf::TextFormat::PrintToString(lm0, &act_lm0);
+ std::string sqact0 = squeezeWhite(act_lm0, "actual for lm 0");
+ const std::string expected_lm0 = RAW_RESULT(
+ name: "/data/app/com.google.android.apps.plus-1/lib/arm/libcronet.so"
+ );
+ std::string sqexp0 = squeezeWhite(expected_lm0, "expected_lm0");
+ EXPECT_STREQ(sqexp0.c_str(), sqact0.c_str());
+ }
+ { const auto &lm9 = encodedProfile.load_modules(9);
+ std::string act_lm9;
+ ::google::protobuf::TextFormat::PrintToString(lm9, &act_lm9);
+ std::string sqact9 = squeezeWhite(act_lm9, "actual for lm 9");
+ const std::string expected_lm9 = RAW_RESULT(
+ name: "/system/lib/libandroid_runtime.so" build_id: "8164ed7b3a8b8f5a220d027788922510"
+ );
+ std::string sqexp9 = squeezeWhite(expected_lm9, "expected_lm9");
+ EXPECT_STREQ(sqexp9.c_str(), sqact9.c_str());
+ }
+
+ // Examine some of the samples now
+ { const auto &p1 = encodedProfile.programs(0);
+ const auto &lm1 = p1.modules(0);
+ std::string act_lm1;
+ ::google::protobuf::TextFormat::PrintToString(lm1, &act_lm1);
+ std::string sqact1 = squeezeWhite(act_lm1, "actual for lm1");
+ const std::string expected_lm1 = RAW_RESULT(
+ load_module_id: 9 address_samples { address: 296100 count: 1 }
+ );
+ std::string sqexp1 = squeezeWhite(expected_lm1, "expected_lm1");
+ EXPECT_STREQ(sqexp1.c_str(), sqact1.c_str());
+ }
+ { const auto &p1 = encodedProfile.programs(2);
+ const auto &lm2 = p1.modules(0);
+ std::string act_lm2;
+ ::google::protobuf::TextFormat::PrintToString(lm2, &act_lm2);
+ std::string sqact2 = squeezeWhite(act_lm2, "actual for lm2");
+ const std::string expected_lm2 = RAW_RESULT(
+ load_module_id: 2
+ address_samples { address: 28030244 count: 1 }
+ address_samples { address: 29657840 count: 1 }
+ );
+ std::string sqexp2 = squeezeWhite(expected_lm2, "expected_lm2");
+ EXPECT_STREQ(sqexp2.c_str(), sqact2.c_str());
+ }
+}
+
+TEST_F(PerfProfdTest, BasicRunWithLivePerf)
+{
+ //
+ // Basic test to exercise the main loop of the daemon. It includes
+ // a live 'perf' run
+ //
+ PerfProfdRunner runner;
+ runner.addToConfig("only_debug_build=0");
+ std::string ddparam("destination_directory="); ddparam += dest_dir;
+ runner.addToConfig(ddparam);
+ runner.addToConfig("main_loop_iterations=1");
+ runner.addToConfig("use_fixed_seed=12345678");
+ runner.addToConfig("collection_interval=9999");
+ runner.addToConfig("stack_profile=1");
+ runner.addToConfig("sample_duration=5");
+
+ // Create semaphore file
+ runner.create_semaphore_file();
+
+ // Kick off daemon
+ int daemon_main_return_code = runner.invoke();
+
+ // Check return code from daemon
+ EXPECT_EQ(0, daemon_main_return_code);
+
+ // Read and decode the resulting perf.data.encoded file
+ wireless_android_play_playlog::AndroidPerfProfile encodedProfile;
+ readEncodedProfile("BasicRunWithLivePerf", encodedProfile);
+
+ // Examine what we get back. Since it's a live profile, we can't
+ // really do much in terms of verifying the contents.
+ EXPECT_LT(0, encodedProfile.programs_size());
+
+ // Verify log contents
+ const std::string expected = RAW_RESULT(
+ I: starting Android Wide Profiling daemon
+ I: config file path set to /data/nativetest/perfprofd_test/perfprofd.conf
+ I: random seed set to 12345678
+ I: sleep 674 seconds
+ I: initiating profile collection
+ I: profile collection complete
+ I: sleep 9325 seconds
+ I: finishing Android Wide Profiling daemon
+ );
+ // check to make sure log excerpt matches
+ compareLogMessages(mock_perfprofdutils_getlogged(),
+ expected, "BasicRunWithLivePerf", true);
+}
+
+int main(int argc, char **argv) {
+ executable_path = argv[0];
+ // switch to / before starting testing (perfprofd
+ // should be location-independent)
+ chdir("/");
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/perfprofd/tests/perfprofdmockutils.cc b/perfprofd/tests/perfprofdmockutils.cc
new file mode 100644
index 00000000..cc4ea94a
--- /dev/null
+++ b/perfprofd/tests/perfprofdmockutils.cc
@@ -0,0 +1,105 @@
+/*
+**
+** Copyright 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.
+*/
+
+#define LOG_TAG "perfprofd"
+
+#include <stdarg.h>
+#include <unistd.h>
+#include <vector>
+#include <string>
+#include <assert.h>
+
+#include <utils/Log.h>
+
+#include "perfprofdutils.h"
+
+static std::vector<std::string> *mock_log;
+
+static void append_to_log(const std::string &s)
+{
+ assert(mock_log);
+ mock_log->push_back(s);
+}
+
+void mock_perfprofdutils_init()
+{
+ assert(!mock_log);
+ mock_log = new std::vector<std::string>;
+}
+
+void mock_perfprofdutils_finish()
+{
+ assert(mock_log);
+ delete mock_log;
+}
+
+std::string mock_perfprofdutils_getlogged()
+{
+ std::string result;
+ assert(mock_log);
+ for (const std::string &s : (*mock_log)) {
+ result += s;
+ }
+ return result;
+}
+
+extern "C" {
+
+#define LMAX 8192
+
+void perfprofd_mocklog(const char *tag, const char *fmt, va_list ap)
+{
+ char buffer[LMAX];
+ strcpy(buffer, tag);
+ vsnprintf(buffer+strlen(tag), LMAX, fmt, ap);
+ std::string b(buffer); b += "\012";
+ append_to_log(b);
+}
+
+void perfprofd_log_error(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap); fprintf(stderr, "\n");
+ perfprofd_mocklog("E: ", fmt, ap);
+ va_end(ap);
+}
+
+void perfprofd_log_warning(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap); fprintf(stderr, "\n");
+ perfprofd_mocklog("W: ", fmt, ap);
+ va_end(ap);
+}
+
+void perfprofd_log_info(const char *fmt, ...)
+{
+ va_list ap;
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap); fprintf(stderr, "\n");
+ perfprofd_mocklog("I: ", fmt, ap);
+ va_end(ap);
+}
+
+void perfprofd_sleep(int seconds)
+{
+ perfprofd_log_info("sleep %d seconds", seconds);
+}
+
+}
diff --git a/perfprofd/tests/perfprofdmockutils.h b/perfprofd/tests/perfprofdmockutils.h
new file mode 100644
index 00000000..12caabb7
--- /dev/null
+++ b/perfprofd/tests/perfprofdmockutils.h
@@ -0,0 +1,31 @@
+/*
+**
+** Copyright 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.
+*/
+
+///
+/// Set up mock utilities layer prior to unit test execution
+///
+extern void mock_perfprofdutils_init();
+
+///
+/// Set up mock utilities layer prior to unit test execution
+///
+extern void mock_perfprofdutils_finish();
+
+///
+/// Return string containing things logged to logd, plus sleep instances
+///
+extern std::string mock_perfprofdutils_getlogged();