Commit d19f5200 authored by wittman's avatar wittman Committed by Commit bot

Metrics provider for statistical stack profiler

Provides a metrics provider that uploads statistical stack profiling
state via UMA.

This CL builds on top of the statistical profiler implementation in
https://crrev.com/1016563004, which is under review.

BUG=464929

Review URL: https://codereview.chromium.org/981143006

Cr-Commit-Position: refs/heads/master@{#321928}
parent 45816be8
...@@ -65,6 +65,10 @@ void PendingProfiles::GetProfiles( ...@@ -65,6 +65,10 @@ void PendingProfiles::GetProfiles(
} // namespace } // namespace
StackSamplingProfiler::Module::Module() : base_address(nullptr) {} StackSamplingProfiler::Module::Module() : base_address(nullptr) {}
StackSamplingProfiler::Module::Module(const void* base_address,
const std::string& id,
const FilePath& filename)
: base_address(base_address), id(id), filename(filename) {}
StackSamplingProfiler::Module::~Module() {} StackSamplingProfiler::Module::~Module() {}
...@@ -72,6 +76,11 @@ StackSamplingProfiler::Frame::Frame() ...@@ -72,6 +76,11 @@ StackSamplingProfiler::Frame::Frame()
: instruction_pointer(nullptr), : instruction_pointer(nullptr),
module_index(-1) {} module_index(-1) {}
StackSamplingProfiler::Frame::Frame(const void* instruction_pointer,
int module_index)
: instruction_pointer(instruction_pointer),
module_index(module_index) {}
StackSamplingProfiler::Frame::~Frame() {} StackSamplingProfiler::Frame::~Frame() {}
StackSamplingProfiler::Profile::Profile() : preserve_sample_ordering(false) {} StackSamplingProfiler::Profile::Profile() : preserve_sample_ordering(false) {}
......
...@@ -53,6 +53,8 @@ class BASE_EXPORT StackSamplingProfiler { ...@@ -53,6 +53,8 @@ class BASE_EXPORT StackSamplingProfiler {
// Module represents the module (DLL or exe) corresponding to a stack frame. // Module represents the module (DLL or exe) corresponding to a stack frame.
struct BASE_EXPORT Module { struct BASE_EXPORT Module {
Module(); Module();
Module(const void* base_address, const std::string& id,
const FilePath& filename);
~Module(); ~Module();
// Points to the base address of the module. // Points to the base address of the module.
...@@ -72,6 +74,7 @@ class BASE_EXPORT StackSamplingProfiler { ...@@ -72,6 +74,7 @@ class BASE_EXPORT StackSamplingProfiler {
// Frame represents an individual sampled stack frame with module information. // Frame represents an individual sampled stack frame with module information.
struct BASE_EXPORT Frame { struct BASE_EXPORT Frame {
Frame(); Frame();
Frame(const void* instruction_pointer, int module_index);
~Frame(); ~Frame();
// The sampled instruction pointer within the function. // The sampled instruction pointer within the function.
......
...@@ -32,6 +32,7 @@ ...@@ -32,6 +32,7 @@
#include "chrome/common/crash_keys.h" #include "chrome/common/crash_keys.h"
#include "chrome/common/pref_names.h" #include "chrome/common/pref_names.h"
#include "chrome/common/render_messages.h" #include "chrome/common/render_messages.h"
#include "components/metrics/call_stack_profile_metrics_provider.h"
#include "components/metrics/gpu/gpu_metrics_provider.h" #include "components/metrics/gpu/gpu_metrics_provider.h"
#include "components/metrics/metrics_service.h" #include "components/metrics/metrics_service.h"
#include "components/metrics/net/net_metrics_log_uploader.h" #include "components/metrics/net/net_metrics_log_uploader.h"
...@@ -334,6 +335,10 @@ void ChromeMetricsServiceClient::Initialize() { ...@@ -334,6 +335,10 @@ void ChromeMetricsServiceClient::Initialize() {
metrics_service_->RegisterMetricsProvider( metrics_service_->RegisterMetricsProvider(
scoped_ptr<metrics::MetricsProvider>(profiler_metrics_provider_)); scoped_ptr<metrics::MetricsProvider>(profiler_metrics_provider_));
metrics_service_->RegisterMetricsProvider(
scoped_ptr<metrics::MetricsProvider>(
new metrics::CallStackProfileMetricsProvider));
#if defined(OS_ANDROID) #if defined(OS_ANDROID)
metrics_service_->RegisterMetricsProvider( metrics_service_->RegisterMetricsProvider(
scoped_ptr<metrics::MetricsProvider>( scoped_ptr<metrics::MetricsProvider>(
......
...@@ -243,6 +243,7 @@ ...@@ -243,6 +243,7 @@
'login/screens/screen_context_unittest.cc', 'login/screens/screen_context_unittest.cc',
], ],
'metrics_unittest_sources': [ 'metrics_unittest_sources': [
'metrics/call_stack_profile_metrics_provider_unittest.cc',
'metrics/compression_utils_unittest.cc', 'metrics/compression_utils_unittest.cc',
'metrics/daily_event_unittest.cc', 'metrics/daily_event_unittest.cc',
'metrics/gpu/gpu_metrics_provider_unittest.cc', 'metrics/gpu/gpu_metrics_provider_unittest.cc',
......
...@@ -23,6 +23,8 @@ ...@@ -23,6 +23,8 @@
'component_metrics_proto', 'component_metrics_proto',
], ],
'sources': [ 'sources': [
'metrics/call_stack_profile_metrics_provider.cc',
'metrics/call_stack_profile_metrics_provider.h',
'metrics/clean_exit_beacon.cc', 'metrics/clean_exit_beacon.cc',
'metrics/clean_exit_beacon.h', 'metrics/clean_exit_beacon.h',
'metrics/client_info.cc', 'metrics/client_info.cc',
...@@ -158,6 +160,7 @@ ...@@ -158,6 +160,7 @@
'target_name': 'component_metrics_proto', 'target_name': 'component_metrics_proto',
'type': 'static_library', 'type': 'static_library',
'sources': [ 'sources': [
'metrics/proto/call_stack_profile.proto',
'metrics/proto/cast_logs.proto', 'metrics/proto/cast_logs.proto',
'metrics/proto/chrome_user_metrics_extension.proto', 'metrics/proto/chrome_user_metrics_extension.proto',
'metrics/proto/histogram_event.proto', 'metrics/proto/histogram_event.proto',
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
# GYP version: components/metrics.gypi:metrics # GYP version: components/metrics.gypi:metrics
source_set("metrics") { source_set("metrics") {
sources = [ sources = [
"call_stack_profile_metrics_provider.cc",
"call_stack_profile_metrics_provider.h",
"clean_exit_beacon.cc", "clean_exit_beacon.cc",
"clean_exit_beacon.h", "clean_exit_beacon.h",
"client_info.cc", "client_info.cc",
......
// Copyright 2015 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.
#include "components/metrics/call_stack_profile_metrics_provider.h"
#include <cstring>
#include <map>
#include <utility>
#include "base/logging.h"
#include "base/macros.h"
#include "base/profiler/stack_sampling_profiler.h"
#include "components/metrics/metrics_hashes.h"
#include "components/metrics/proto/chrome_user_metrics_extension.pb.h"
using base::StackSamplingProfiler;
namespace metrics {
namespace {
// The protobuf expects the MD5 checksum prefix of the module name.
uint64 HashModuleFilename(const base::FilePath& filename) {
const base::FilePath::StringType basename = filename.BaseName().value();
// Copy the bytes in basename into a string buffer.
size_t basename_length_in_bytes =
basename.size() * sizeof(base::FilePath::CharType);
std::string name_bytes(basename_length_in_bytes, '\0');
memcpy(&name_bytes[0], &basename[0], basename_length_in_bytes);
return HashMetricName(name_bytes);
}
// Transcode |sample| into |proto_sample|, using base addresses in |modules| to
// compute module instruction pointer offsets.
void CopySampleToProto(
const StackSamplingProfiler::Sample& sample,
const std::vector<StackSamplingProfiler::Module>& modules,
CallStackProfile::Sample* proto_sample) {
for (const StackSamplingProfiler::Frame& frame : sample) {
CallStackProfile::Entry* entry = proto_sample->add_entry();
// A frame may not have a valid module. If so, we can't compute the
// instruction pointer offset, and we don't want to send bare pointers, so
// leave call_stack_entry empty.
if (frame.module_index < 0)
continue;
int64 module_offset =
reinterpret_cast<const char*>(frame.instruction_pointer) -
reinterpret_cast<const char*>(modules[frame.module_index].base_address);
DCHECK_GE(module_offset, 0);
entry->set_address(static_cast<uint64>(module_offset));
entry->set_module_id_index(frame.module_index);
}
}
// Transcode |profile| into |proto_profile|.
void CopyProfileToProto(
const StackSamplingProfiler::Profile& profile,
CallStackProfile* proto_profile) {
if (profile.samples.empty())
return;
if (profile.preserve_sample_ordering) {
// Collapse only consecutive repeated samples together.
CallStackProfile::Sample* current_sample_proto = nullptr;
for (auto it = profile.samples.begin(); it != profile.samples.end(); ++it) {
if (!current_sample_proto || *it != *(it - 1)) {
current_sample_proto = proto_profile->add_sample();
CopySampleToProto(*it, profile.modules, current_sample_proto);
current_sample_proto->set_count(1);
} else {
current_sample_proto->set_count(current_sample_proto->count() + 1);
}
}
} else {
// Collapse all repeated samples together.
std::map<StackSamplingProfiler::Sample, int> sample_index;
for (auto it = profile.samples.begin(); it != profile.samples.end(); ++it) {
auto location = sample_index.find(*it);
if (location == sample_index.end()) {
CallStackProfile::Sample* sample_proto = proto_profile->add_sample();
CopySampleToProto(*it, profile.modules, sample_proto);
sample_proto->set_count(1);
sample_index.insert(
std::make_pair(
*it, static_cast<int>(proto_profile->sample().size()) - 1));
} else {
CallStackProfile::Sample* sample_proto =
proto_profile->mutable_sample()->Mutable(location->second);
sample_proto->set_count(sample_proto->count() + 1);
}
}
}
for (const StackSamplingProfiler::Module& module : profile.modules) {
CallStackProfile::ModuleIdentifier* module_id =
proto_profile->add_module_id();
module_id->set_build_id(module.id);
module_id->set_name_md5_prefix(HashModuleFilename(module.filename));
}
proto_profile->set_profile_duration_ms(
profile.profile_duration.InMilliseconds());
proto_profile->set_sampling_period_ms(
profile.sampling_period.InMilliseconds());
}
} // namespace
CallStackProfileMetricsProvider::CallStackProfileMetricsProvider() {}
CallStackProfileMetricsProvider::~CallStackProfileMetricsProvider() {}
void CallStackProfileMetricsProvider::ProvideGeneralMetrics(
ChromeUserMetricsExtension* uma_proto) {
std::vector<StackSamplingProfiler::Profile> profiles;
if (!source_profiles_for_test_.empty())
profiles.swap(source_profiles_for_test_);
else
StackSamplingProfiler::GetPendingProfiles(&profiles);
for (const StackSamplingProfiler::Profile& profile : profiles) {
CallStackProfile* call_stack_profile =
uma_proto->add_sampled_profile()->mutable_call_stack_profile();
CopyProfileToProto(profile, call_stack_profile);
}
}
void CallStackProfileMetricsProvider::SetSourceProfilesForTesting(
const std::vector<StackSamplingProfiler::Profile>& profiles) {
source_profiles_for_test_ = profiles;
}
} // namespace metrics
// Copyright 2015 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 COMPONENTS_METRICS_CALL_STACK_PROFILE_METRICS_PROVIDER_H_
#define COMPONENTS_METRICS_CALL_STACK_PROFILE_METRICS_PROVIDER_H_
#include <vector>
#include "base/profiler/stack_sampling_profiler.h"
#include "components/metrics/metrics_provider.h"
namespace metrics {
class ChromeUserMetricsExtension;
// Performs metrics logging for the stack sampling profiler.
class CallStackProfileMetricsProvider : public MetricsProvider {
public:
CallStackProfileMetricsProvider();
~CallStackProfileMetricsProvider() override;
// MetricsProvider:
void ProvideGeneralMetrics(ChromeUserMetricsExtension* uma_proto) override;
// Uses |profiles| as the source data for the next invocation of
// ProvideGeneralMetrics, rather than sourcing them from the
// StackSamplingProfiler.
void SetSourceProfilesForTesting(
const std::vector<base::StackSamplingProfiler::Profile>& profiles);
private:
std::vector<base::StackSamplingProfiler::Profile> source_profiles_for_test_;
DISALLOW_COPY_AND_ASSIGN(CallStackProfileMetricsProvider);
};
} // namespace metrics
#endif // COMPONENTS_METRICS_CALL_STACK_PROFILE_METRICS_PROVIDER_H_
...@@ -7,6 +7,7 @@ import("//third_party/protobuf/proto_library.gni") ...@@ -7,6 +7,7 @@ import("//third_party/protobuf/proto_library.gni")
# GYP version: components/ # GYP version: components/
proto_library("proto") { proto_library("proto") {
sources = [ sources = [
"call_stack_profile.proto",
"cast_logs.proto", "cast_logs.proto",
"chrome_user_metrics_extension.proto", "chrome_user_metrics_extension.proto",
"histogram_event.proto", "histogram_event.proto",
......
// Copyright 2015 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.
// Call stack sample data for a given profiling session.
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
package metrics;
// Next tag: 5
message CallStackProfile {
// Describes an entry in the callstack.
message Entry {
// Instruction pointer subtracted by module base.
optional uint64 address = 1;
// Index to the module identifier in |module_ids| of CallStackProfile.
optional int32 module_id_index = 2;
}
// A sample consisting of one or more callstacks with the same stack frames
// and instruction pointers.
message Sample {
// The callstack. Sample.entries[0] represents the call on the top of the
// stack.
repeated Entry entry = 1;
// Number of times this stack signature occurs.
optional int64 count = 2;
}
// Uniquely identifies a module.
message ModuleIdentifier {
// A hash that uniquely identifies a particular program version with high
// probability. This is parsed from headers of the loaded module.
// For binaries generated by GNU tools:
// Contents of the .note.gnu.build-id field.
// On Windows:
// GUID + AGE in the debug image headers of a module.
optional bytes build_id = 1;
// MD5Sum Prefix of the module name. This is the same hashing scheme as used
// to hash UMA histogram names.
optional fixed64 name_md5_prefix = 2;
}
// The callstack and counts.
repeated Sample sample = 1;
// List of module ids found in this sample.
repeated ModuleIdentifier module_id = 2;
// Duration of this profile.
optional int32 profile_duration_ms = 3;
// Time between samples.
optional int32 sampling_period_ms = 4;
}
...@@ -8,12 +8,13 @@ option optimize_for = LITE_RUNTIME; ...@@ -8,12 +8,13 @@ option optimize_for = LITE_RUNTIME;
package metrics; package metrics;
import "call_stack_profile.proto";
import "perf_data.proto"; import "perf_data.proto";
// Protocol buffer for collected sample-based profiling data. // Protocol buffer for collected sample-based profiling data.
// Contains the parameters and data from a single profile collection event. // Contains the parameters and data from a single profile collection event.
// Next tag: 9 // Next tag: 10
message SampledProfile { message SampledProfile {
// Indicates the event that triggered this collection. // Indicates the event that triggered this collection.
enum TriggerEvent { enum TriggerEvent {
...@@ -59,6 +60,9 @@ message SampledProfile { ...@@ -59,6 +60,9 @@ message SampledProfile {
// collected. Only set when |trigger_event| is RESTORE_SESSION. // collected. Only set when |trigger_event| is RESTORE_SESSION.
optional int64 ms_after_restore = 8; optional int64 ms_after_restore = 8;
// The actual perf data that was collected. // Sampled profile data collected from Linux perf tool.
optional PerfDataProto perf_data = 4; optional PerfDataProto perf_data = 4;
// Sampled profile data collected by periodic sampling of call stacks.
optional CallStackProfile call_stack_profile = 9;
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment