Commit 4dec7e4b authored by Xi Cheng's avatar Xi Cheng Committed by Commit Bot

Directly write SampledProfile protocol buffer message and send over mojo

This change removes the current execution profile _mojo_ representation
in favor of directly writing the data in the protocol buffer format used
for UMA upload. The reasons for using this format are:
1. it's the format that we ultimately need the data in;
2. it's the most memory-efficient representation available to us, because
   of the protocol buffer varint encoding;
3. the serialized protocol buffers are trivially compressible.

We generate execution profiles in non-browser processes, so this means
writing the protocol buffer representation in those processes and sending
over mojo to the browser process, where the UMA upload takes place.

We're making this change as part of a refactoring to expand the profiling
scenarios supported by the UMA Sampling Profiler. A key aspect of this
refactoring is reducing memory consumption of the execution profile
representation. See http://crbug.com/850148 for examples of memory usage
problems with this representation.

Bug: 851163, 804942
Change-Id: Ied88d6fd932587707c1f5a4c79acd77d5911f6ee
Reviewed-on: https://chromium-review.googlesource.com/1157547
Commit-Queue: Mike Wittman <wittman@chromium.org>
Reviewed-by: default avatarSteven Holte <holte@chromium.org>
Reviewed-by: default avatarMike Wittman <wittman@chromium.org>
Reviewed-by: default avatarFrançois Doray <fdoray@chromium.org>
Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Cr-Commit-Position: refs/heads/master@{#582209}
parent ce3a5818
......@@ -136,7 +136,6 @@
#include "components/language/core/common/language_experiments.h"
#include "components/language_usage_metrics/language_usage_metrics.h"
#include "components/metrics/call_stack_profile_builder.h"
#include "components/metrics/call_stack_profile_metrics_provider.h"
#include "components/metrics/call_stack_profile_params.h"
#include "components/metrics/expired_histogram_util.h"
#include "components/metrics/metrics_reporting_default_state.h"
......@@ -2114,7 +2113,7 @@ void ChromeBrowserMainParts::PostMainMessageLoopRun() {
void ChromeBrowserMainParts::PreShutdown() {
metrics::CallStackProfileBuilder::SetProcessMilestone(
metrics::CallStackProfileMetricsProvider::SHUTDOWN_START);
metrics::CallStackProfileBuilder::SHUTDOWN_START);
}
void ChromeBrowserMainParts::PostDestroyThreads() {
......
......@@ -57,6 +57,9 @@ include_rules = [
"+third_party/blink/public/platform/web_client_hints_types.mojom.h",
"+third_party/blink/public/platform/web_feature.mojom.h",
# Temporary dependency for thread_profiler.h. TODO(chengx): remove this dependency.
"+third_party/metrics_proto/sampled_profile.pb.h",
# FIXME - refactor code and remove these dependencies
"+chrome/installer/util",
]
......@@ -196,12 +196,12 @@ ThreadProfiler::ThreadProfiler(
if (!StackSamplingConfiguration::Get()->IsProfilerEnabledForCurrentProcess())
return;
auto profile_builder =
std::make_unique<CallStackProfileBuilder>(BindRepeating(
&ThreadProfiler::ReceiveStartupProfile,
GetReceiverCallback(CallStackProfileParams(
GetProcess(), thread, CallStackProfileParams::PROCESS_STARTUP,
CallStackProfileParams::MAY_SHUFFLE))));
auto profile_builder = std::make_unique<CallStackProfileBuilder>(
BindRepeating(&ThreadProfiler::ReceiveStartupProfile,
GetReceiverCallback()),
CallStackProfileParams(GetProcess(), thread,
CallStackProfileParams::PROCESS_STARTUP,
CallStackProfileParams::MAY_SHUFFLE));
startup_profiler_ = std::make_unique<StackSamplingProfiler>(
base::PlatformThread::CurrentId(), kSamplingParams,
......@@ -225,8 +225,8 @@ ThreadProfiler::ThreadProfiler(
ScheduleNextPeriodicCollection();
}
CallStackProfileBuilder::CompletedCallback ThreadProfiler::GetReceiverCallback(
const CallStackProfileParams& profile_params) {
CallStackProfileBuilder::CompletedCallback
ThreadProfiler::GetReceiverCallback() {
// TODO(wittman): Simplify the approach to getting the profiler callback
// across CallStackProfileMetricsProvider and
// ChildCallStackProfileCollector. Ultimately both should expose functions
......@@ -242,17 +242,16 @@ CallStackProfileBuilder::CompletedCallback ThreadProfiler::GetReceiverCallback(
base::TimeTicks profile_start_time = base::TimeTicks::Now();
if (GetProcess() == CallStackProfileParams::BROWSER_PROCESS) {
return metrics::CallStackProfileMetricsProvider::
GetProfilerCallbackForBrowserProcess(profile_params);
GetProfilerCallbackForBrowserProcess();
}
return g_child_call_stack_profile_collector.Get()
.ChildCallStackProfileCollector::GetProfilerCallback(profile_params,
profile_start_time);
.ChildCallStackProfileCollector::GetProfilerCallback(profile_start_time);
}
// static
void ThreadProfiler::ReceiveStartupProfile(
const CallStackProfileBuilder::CompletedCallback& receiver_callback,
StackSamplingProfiler::CallStackProfile profile) {
metrics::SampledProfile profile) {
receiver_callback.Run(std::move(profile));
}
......@@ -261,7 +260,7 @@ void ThreadProfiler::ReceivePeriodicProfile(
const CallStackProfileBuilder::CompletedCallback& receiver_callback,
scoped_refptr<base::SingleThreadTaskRunner> owning_thread_task_runner,
base::WeakPtr<ThreadProfiler> thread_profiler,
StackSamplingProfiler::CallStackProfile profile) {
metrics::SampledProfile profile) {
receiver_callback.Run(std::move(profile));
owning_thread_task_runner->PostTask(
FROM_HERE, base::BindOnce(&ThreadProfiler::ScheduleNextPeriodicCollection,
......@@ -282,8 +281,9 @@ void ThreadProfiler::StartPeriodicSamplingCollection() {
// NB: Destroys the previous profiler as side effect.
auto profile_builder = std::make_unique<CallStackProfileBuilder>(
BindRepeating(&ThreadProfiler::ReceivePeriodicProfile,
GetReceiverCallback(periodic_profile_params_),
owning_thread_task_runner_, weak_factory_.GetWeakPtr()));
GetReceiverCallback(), owning_thread_task_runner_,
weak_factory_.GetWeakPtr()),
periodic_profile_params_);
periodic_profiler_ = std::make_unique<StackSamplingProfiler>(
base::PlatformThread::CurrentId(), kSamplingParams,
......
......@@ -17,6 +17,7 @@
#include "base/time/time.h"
#include "components/metrics/call_stack_profile_builder.h"
#include "components/metrics/call_stack_profile_params.h"
#include "third_party/metrics_proto/sampled_profile.pb.h"
namespace service_manager {
class Connector;
......@@ -93,8 +94,7 @@ class ThreadProfiler {
scoped_refptr<base::SingleThreadTaskRunner>());
// Gets the completed callback for the ultimate receiver of the profile.
metrics::CallStackProfileBuilder::CompletedCallback GetReceiverCallback(
const metrics::CallStackProfileParams& profile_params);
metrics::CallStackProfileBuilder::CompletedCallback GetReceiverCallback();
// Receives |profile| from the metrics::CallStackProfileBuilder and forwards
// it on to the original |receiver_callback|. Note that we must obtain and
......@@ -105,13 +105,13 @@ class ThreadProfiler {
static void ReceiveStartupProfile(
const metrics::CallStackProfileBuilder::CompletedCallback&
receiver_callback,
base::StackSamplingProfiler::CallStackProfile profile);
metrics::SampledProfile profile);
static void ReceivePeriodicProfile(
const metrics::CallStackProfileBuilder::CompletedCallback&
receiver_callback,
scoped_refptr<base::SingleThreadTaskRunner> owning_thread_task_runner,
base::WeakPtr<ThreadProfiler> thread_profiler,
base::StackSamplingProfiler::CallStackProfile profile);
metrics::SampledProfile profile);
// Posts a delayed task to start the next periodic sampling collection.
void ScheduleNextPeriodicCollection();
......
......@@ -262,10 +262,13 @@ source_set("call_stack_profile") {
"call_stack_profile_builder.cc",
"call_stack_profile_builder.h",
"call_stack_profile_params.h",
"call_stack_profile_proto_encoder.cc",
"call_stack_profile_proto_encoder.h",
]
deps = [
"//base:base",
"//third_party/metrics_proto",
]
}
......@@ -277,6 +280,7 @@ source_set("call_stacks") {
deps = [
":metrics",
"//components/metrics/public/interfaces:call_stack_mojo_bindings",
"//third_party/metrics_proto",
]
}
......@@ -289,6 +293,7 @@ source_set("child_call_stacks") {
":call_stack_profile",
"//components/metrics/public/interfaces:call_stack_mojo_bindings",
"//services/service_manager/public/cpp",
"//third_party/metrics_proto",
]
}
......@@ -332,6 +337,7 @@ source_set("unit_tests") {
sources = [
"call_stack_profile_builder_unittest.cc",
"call_stack_profile_metrics_provider_unittest.cc",
"call_stack_profile_proto_encoder_unittest.cc",
"child_call_stack_profile_collector_unittest.cc",
"cloned_install_detector_unittest.cc",
"component_metrics_provider_unittest.cc",
......@@ -383,6 +389,7 @@ source_set("unit_tests") {
"//services/network/public/cpp:cpp",
"//services/service_manager/public/cpp",
"//testing/gtest",
"//third_party/metrics_proto",
"//third_party/zlib/google:compression_utils",
"//ui/gfx/geometry",
]
......
......@@ -5,9 +5,10 @@
#include "components/metrics/call_stack_profile_builder.h"
#include <utility>
#include <vector>
#include "base/atomicops.h"
#include "base/logging.h"
#include "components/metrics/call_stack_profile_proto_encoder.h"
using StackSamplingProfiler = base::StackSamplingProfiler;
......@@ -41,8 +42,9 @@ void ChangeAtomicFlags(base::subtle::Atomic32* flags,
} // namespace
CallStackProfileBuilder::CallStackProfileBuilder(
const CompletedCallback& callback)
: callback_(callback) {}
const CompletedCallback& callback,
const CallStackProfileParams& profile_params)
: callback_(callback), profile_params_(profile_params) {}
CallStackProfileBuilder::~CallStackProfileBuilder() = default;
......@@ -89,8 +91,20 @@ void CallStackProfileBuilder::OnProfileCompleted(
profile_.profile_duration = profile_duration;
profile_.sampling_period = sampling_period;
// Run the associated callback, passing the collected profile.
callback_.Run(std::move(profile_));
// TODO(chengx): build the metrics.SampledProfile protocol message
// incrementally.
SampledProfile sampled_profile;
sampled_profile.set_process(
ToExecutionContextProcess(profile_params_.process));
sampled_profile.set_thread(ToExecutionContextThread(profile_params_.thread));
sampled_profile.set_trigger_event(
ToSampledProfileTriggerEvent(profile_params_.trigger));
CopyProfileToProto(profile_, profile_params_.ordering_spec,
sampled_profile.mutable_call_stack_profile());
// Run the associated callback, passing the protocol message which encodes the
// collected profile.
callback_.Run(sampled_profile);
}
// static
......
......@@ -9,23 +9,42 @@
#include "base/callback.h"
#include "base/profiler/stack_sampling_profiler.h"
#include "components/metrics/call_stack_profile_params.h"
#include "third_party/metrics_proto/sampled_profile.pb.h"
namespace metrics {
// CallStackProfileBuilder builds a CallStackProfile from the collected sampling
// data.
//
// The results of the profile building -- a CallStackProfile, is passed to the
// completed callback. A CallStackProfile contains a set of Samples and
// Modules, and other sampling information. One Sample corresponds to a single
// recorded stack, and the Modules record those modules associated with the
// recorded stack frames.
// The results of the profile building -- a CallStackProfile, is encoded to
// metrics.SampledProfile protocol message. The message is then passed to the
// completed callback. A CallStackProfile contains a set of Samples and Modules,
// and other sampling information. One Sample corresponds to a single recorded
// stack, and the Modules record those modules associated with the recorded
// stack frames.
class CallStackProfileBuilder
: public base::StackSamplingProfiler::ProfileBuilder {
public:
// The callback type used to collect a completed profile. The passed
// CallStackProfile is move-only. Other threads, including the UI thread, may
// block on callback completion so this should run as quickly as possible.
// These milestones of a process lifetime can be passed as process "mile-
// stones" to CallStackProfileBuilder::SetProcessMilestone(). Be sure to
// update the translation constants at the top of the .cc file when this is
// changed.
enum Milestones : int {
MAIN_LOOP_START,
MAIN_NAVIGATION_START,
MAIN_NAVIGATION_FINISHED,
FIRST_NONEMPTY_PAINT,
SHUTDOWN_START,
MILESTONES_MAX_VALUE
};
// The callback type used to collect a metrics.SampledProfile protocol
// message. The passed SampledProfile is move-only. Other threads, including
// the UI thread, may block on callback completion so this should run as
// quickly as possible.
//
// IMPORTANT NOTE: The callback is invoked on a thread the profiler
// constructs, rather than on the thread used to construct the profiler, and
......@@ -33,10 +52,10 @@ class CallStackProfileBuilder
// loops that create CallStackProfileBuilders, posting a task to the message
// loop with the moved (i.e. std::move) profile is the thread-safe callback
// implementation.
using CompletedCallback =
base::Callback<void(base::StackSamplingProfiler::CallStackProfile)>;
using CompletedCallback = base::RepeatingCallback<void(SampledProfile)>;
CallStackProfileBuilder(const CompletedCallback& callback);
CallStackProfileBuilder(const CompletedCallback& callback,
const CallStackProfileParams& profile_params);
~CallStackProfileBuilder() override;
......@@ -68,6 +87,9 @@ class CallStackProfileBuilder
// Callback made when sampling a profile completes.
const CompletedCallback callback_;
// The parameters associated with the sampled profile.
const CallStackProfileParams profile_params_;
DISALLOW_COPY_AND_ASSIGN(CallStackProfileBuilder);
};
......
......@@ -8,7 +8,7 @@
#include <utility>
#include "components/metrics/call_stack_profile_metrics_provider.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "components/metrics/call_stack_profile_proto_encoder.h"
#include "mojo/public/cpp/bindings/strong_binding.h"
namespace metrics {
......@@ -28,15 +28,13 @@ void CallStackProfileCollector::Create(
std::move(request));
}
void CallStackProfileCollector::Collect(const CallStackProfileParams& params,
base::TimeTicks start_timestamp,
CallStackProfile profile) {
if (params.process != expected_process_)
void CallStackProfileCollector::Collect(base::TimeTicks start_timestamp,
SampledProfile profile) {
if (profile.process() != ToExecutionContextProcess(expected_process_))
return;
CallStackProfileParams params_copy = params;
CallStackProfileMetricsProvider::ReceiveCompletedProfile(
params_copy, start_timestamp, std::move(profile));
CallStackProfileMetricsProvider::ReceiveCompletedProfile(start_timestamp,
std::move(profile));
}
} // namespace metrics
......@@ -6,14 +6,14 @@
#define COMPONENTS_METRICS_CALL_STACK_PROFILE_COLLECTOR_H_
#include "base/macros.h"
#include "components/metrics/call_stack_profile_params.h"
#include "components/metrics/public/interfaces/call_stack_profile_collector.mojom.h"
#include "third_party/metrics_proto/sampled_profile.pb.h"
namespace metrics {
class CallStackProfileCollector : public mojom::CallStackProfileCollector {
public:
using CallStackProfile = base::StackSamplingProfiler::CallStackProfile;
explicit CallStackProfileCollector(
CallStackProfileParams::Process expected_process);
~CallStackProfileCollector() override;
......@@ -23,9 +23,8 @@ class CallStackProfileCollector : public mojom::CallStackProfileCollector {
mojom::CallStackProfileCollectorRequest request);
// mojom::CallStackProfileCollector:
void Collect(const CallStackProfileParams& params,
base::TimeTicks start_timestamp,
CallStackProfile profile) override;
void Collect(base::TimeTicks start_timestamp,
SampledProfile profile) override;
private:
// Profile params are validated to come from this process. Profiles with a
......
......@@ -7,11 +7,10 @@
#include "base/feature_list.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/profiler/stack_sampling_profiler.h"
#include "base/time/time.h"
#include "components/metrics/call_stack_profile_builder.h"
#include "components/metrics/call_stack_profile_params.h"
#include "components/metrics/metrics_provider.h"
#include "third_party/metrics_proto/sampled_profile.pb.h"
namespace metrics {
......@@ -20,21 +19,6 @@ class ChromeUserMetricsExtension;
// Performs metrics logging for the stack sampling profiler.
class CallStackProfileMetricsProvider : public MetricsProvider {
public:
// These milestones of a process lifetime can be passed as process "mile-
// stones" to CallStackProfileBuilder::SetProcessMilestone(). Be sure to
// update the translation constants at the top of the .cc file when this is
// changed.
enum Milestones : int {
MAIN_LOOP_START,
MAIN_NAVIGATION_START,
MAIN_NAVIGATION_FINISHED,
FIRST_NONEMPTY_PAINT,
SHUTDOWN_START,
MILESTONES_MAX_VALUE
};
CallStackProfileMetricsProvider();
~CallStackProfileMetricsProvider() override;
......@@ -43,16 +27,14 @@ class CallStackProfileMetricsProvider : public MetricsProvider {
// immediately passed to the CallStackProfileBuilder, and should not be
// reused.
static CallStackProfileBuilder::CompletedCallback
GetProfilerCallbackForBrowserProcess(const CallStackProfileParams& params);
GetProfilerCallbackForBrowserProcess();
// Provides completed stack profile to the metrics provider. Intended for use
// when receiving profiles over IPC. In-process StackSamplingProfiler users
// should instead use a variant of GetProfilerCallback*(). |profile| is not
// const& because it must be passed with std::move.
static void ReceiveCompletedProfile(
const CallStackProfileParams& params,
base::TimeTicks profile_start_time,
base::StackSamplingProfiler::CallStackProfile profile);
static void ReceiveCompletedProfile(base::TimeTicks profile_start_time,
SampledProfile profile);
// MetricsProvider:
void OnRecordingEnabled() override;
......
// Copyright 2018 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_proto_encoder.h"
#include <stddef.h>
#include <cstring>
#include <map>
#include <string>
#include <utility>
#include "base/logging.h"
#include "base/metrics/metrics_hashes.h"
#include "base/stl_util.h"
#include "components/metrics/call_stack_profile_builder.h"
namespace metrics {
namespace {
// Provide a mapping from the C++ "enum" definition of various process mile-
// stones to the equivalent protobuf "enum" definition. This table-lookup
// conversion allows for the implementation to evolve and still be compatible
// with the protobuf -- even if there are ever more than 32 defined proto
// values, though never more than 32 could be in-use in a given C++ version
// of the code.
const ProcessPhase kProtoPhases[CallStackProfileBuilder::MILESTONES_MAX_VALUE] =
{
ProcessPhase::MAIN_LOOP_START,
ProcessPhase::MAIN_NAVIGATION_START,
ProcessPhase::MAIN_NAVIGATION_FINISHED,
ProcessPhase::FIRST_NONEMPTY_PAINT,
ProcessPhase::SHUTDOWN_START,
};
} // namespace
uint64_t 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 base::HashMetricName(name_bytes);
}
void CopySampleToProto(
const base::StackSamplingProfiler::Sample& sample,
const std::vector<base::StackSamplingProfiler::Module>& modules,
CallStackProfile::Sample* proto_sample) {
for (const auto& frame : sample.frames) {
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 == base::kUnknownModuleIndex)
continue;
int64_t 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_t>(module_offset));
entry->set_module_id_index(frame.module_index);
}
}
void CopyAnnotationsToProto(uint32_t new_milestones,
CallStackProfile::Sample* sample_proto) {
for (size_t bit = 0; new_milestones != 0 && bit < sizeof(new_milestones) * 8;
++bit) {
const uint32_t flag = 1U << bit;
if (new_milestones & flag) {
if (bit >= base::size(kProtoPhases)) {
NOTREACHED();
continue;
}
sample_proto->add_process_phase(kProtoPhases[bit]);
new_milestones ^= flag; // Bit is set so XOR will clear it.
}
}
}
void CopyProfileToProto(
const base::StackSamplingProfiler::CallStackProfile& profile,
CallStackProfileParams::SampleOrderingSpec ordering_spec,
CallStackProfile* proto_profile) {
if (profile.samples.empty())
return;
const bool preserve_order =
ordering_spec == CallStackProfileParams::PRESERVE_ORDER;
std::map<base::StackSamplingProfiler::Sample, int> sample_index;
uint32_t milestones = 0;
for (auto it = profile.samples.begin(); it != profile.samples.end(); ++it) {
int existing_sample_index = -1;
if (preserve_order) {
// Collapse sample with the previous one if they match. Samples match
// if the frame and all annotations are the same.
if (proto_profile->sample_size() > 0 && *it == *(it - 1))
existing_sample_index = proto_profile->sample_size() - 1;
} else {
auto location = sample_index.find(*it);
if (location != sample_index.end())
existing_sample_index = location->second;
}
if (existing_sample_index != -1) {
CallStackProfile::Sample* sample_proto =
proto_profile->mutable_sample()->Mutable(existing_sample_index);
sample_proto->set_count(sample_proto->count() + 1);
continue;
}
CallStackProfile::Sample* sample_proto = proto_profile->add_sample();
CopySampleToProto(*it, profile.modules, sample_proto);
sample_proto->set_count(1);
CopyAnnotationsToProto(it->process_milestones & ~milestones, sample_proto);
milestones = it->process_milestones;
if (!preserve_order) {
sample_index.insert(std::make_pair(
*it, static_cast<int>(proto_profile->sample_size()) - 1));
}
}
for (const auto& 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());
}
Process ToExecutionContextProcess(CallStackProfileParams::Process process) {
switch (process) {
case CallStackProfileParams::UNKNOWN_PROCESS:
return UNKNOWN_PROCESS;
case CallStackProfileParams::BROWSER_PROCESS:
return BROWSER_PROCESS;
case CallStackProfileParams::RENDERER_PROCESS:
return RENDERER_PROCESS;
case CallStackProfileParams::GPU_PROCESS:
return GPU_PROCESS;
case CallStackProfileParams::UTILITY_PROCESS:
return UTILITY_PROCESS;
case CallStackProfileParams::ZYGOTE_PROCESS:
return ZYGOTE_PROCESS;
case CallStackProfileParams::SANDBOX_HELPER_PROCESS:
return SANDBOX_HELPER_PROCESS;
case CallStackProfileParams::PPAPI_PLUGIN_PROCESS:
return PPAPI_PLUGIN_PROCESS;
case CallStackProfileParams::PPAPI_BROKER_PROCESS:
return PPAPI_BROKER_PROCESS;
}
NOTREACHED();
return UNKNOWN_PROCESS;
}
Thread ToExecutionContextThread(CallStackProfileParams::Thread thread) {
switch (thread) {
case CallStackProfileParams::UNKNOWN_THREAD:
return UNKNOWN_THREAD;
case CallStackProfileParams::MAIN_THREAD:
return MAIN_THREAD;
case CallStackProfileParams::IO_THREAD:
return IO_THREAD;
case CallStackProfileParams::COMPOSITOR_THREAD:
return COMPOSITOR_THREAD;
}
NOTREACHED();
return UNKNOWN_THREAD;
}
SampledProfile::TriggerEvent ToSampledProfileTriggerEvent(
CallStackProfileParams::Trigger trigger) {
switch (trigger) {
case CallStackProfileParams::UNKNOWN:
return SampledProfile::UNKNOWN_TRIGGER_EVENT;
case CallStackProfileParams::PROCESS_STARTUP:
return SampledProfile::PROCESS_STARTUP;
case CallStackProfileParams::JANKY_TASK:
return SampledProfile::JANKY_TASK;
case CallStackProfileParams::THREAD_HUNG:
return SampledProfile::THREAD_HUNG;
case CallStackProfileParams::PERIODIC_COLLECTION:
return SampledProfile::PERIODIC_COLLECTION;
}
NOTREACHED();
return SampledProfile::UNKNOWN_TRIGGER_EVENT;
}
} // namespace metrics
// Copyright 2018 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_PROTO_ENCODER_H_
#define COMPONENTS_METRICS_CALL_STACK_PROFILE_PROTO_ENCODER_H_
#include <vector>
#include "base/files/file_path.h"
#include "base/profiler/stack_sampling_profiler.h"
#include "components/metrics/call_stack_profile_params.h"
#include "third_party/metrics_proto/sampled_profile.pb.h"
namespace metrics {
// These functions are used to encode protobufs.
// The protobuf expects the MD5 checksum prefix of the module name.
uint64_t HashModuleFilename(const base::FilePath& filename);
// Transcode |sample| into |proto_sample|, using base addresses in |modules| to
// compute module instruction pointer offsets.
void CopySampleToProto(
const base::StackSamplingProfiler::Sample& sample,
const std::vector<base::StackSamplingProfiler::Module>& modules,
CallStackProfile::Sample* proto_sample);
// Transcode Sample annotations into protobuf fields. The C++ code uses a bit-
// field with each bit corresponding to an entry in an enumeration while the
// protobuf uses a repeated field of individual values. Conversion tables
// allow for arbitrary mapping, though no more than 32 in any given version
// of the code.
void CopyAnnotationsToProto(uint32_t new_milestones,
CallStackProfile::Sample* sample_proto);
// Transcode |profile| into |proto_profile|.
void CopyProfileToProto(
const base::StackSamplingProfiler::CallStackProfile& profile,
CallStackProfileParams::SampleOrderingSpec ordering_spec,
CallStackProfile* proto_profile);
// Translates CallStackProfileParams's process to the corresponding
// execution context Process.
Process ToExecutionContextProcess(CallStackProfileParams::Process process);
// Translates CallStackProfileParams's thread to the corresponding
// SampledProfile Thread.
Thread ToExecutionContextThread(CallStackProfileParams::Thread thread);
// Translates CallStackProfileParams's trigger to the corresponding
// SampledProfile TriggerEvent.
SampledProfile::TriggerEvent ToSampledProfileTriggerEvent(
CallStackProfileParams::Trigger trigger);
} // namespace metrics
#endif // COMPONENTS_METRICS_CALL_STACK_PROFILE_PROTO_ENCODER_H_
......@@ -7,11 +7,10 @@
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/logging.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread_task_runner_handle.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#include "base/time/time.h"
namespace metrics {
......@@ -20,12 +19,9 @@ ChildCallStackProfileCollector::ProfileState::ProfileState(ProfileState&&) =
default;
ChildCallStackProfileCollector::ProfileState::ProfileState(
const CallStackProfileParams& params,
base::TimeTicks start_timestamp,
base::StackSamplingProfiler::CallStackProfile profile)
: params(params),
start_timestamp(start_timestamp),
profile(std::move(profile)) {}
SampledProfile profile)
: start_timestamp(start_timestamp), profile(std::move(profile)) {}
ChildCallStackProfileCollector::ProfileState::~ProfileState() = default;
......@@ -40,11 +36,10 @@ ChildCallStackProfileCollector::~ChildCallStackProfileCollector() {}
CallStackProfileBuilder::CompletedCallback
ChildCallStackProfileCollector::GetProfilerCallback(
const CallStackProfileParams& params,
base::TimeTicks profile_start_time) {
return base::Bind(&ChildCallStackProfileCollector::Collect,
// This class has lazy instance lifetime.
base::Unretained(this), params, profile_start_time);
base::Unretained(this), profile_start_time);
}
void ChildCallStackProfileCollector::SetParentProfileCollector(
......@@ -60,26 +55,23 @@ void ChildCallStackProfileCollector::SetParentProfileCollector(
parent_collector_ = std::move(parent_collector);
if (parent_collector_) {
for (ProfileState& state : profiles_) {
parent_collector_->Collect(state.params, state.start_timestamp,
parent_collector_->Collect(state.start_timestamp,
std::move(state.profile));
}
}
profiles_.clear();
}
void ChildCallStackProfileCollector::Collect(
const CallStackProfileParams& params,
base::TimeTicks start_timestamp,
CallStackProfile profile) {
void ChildCallStackProfileCollector::Collect(base::TimeTicks start_timestamp,
SampledProfile profile) {
// Impl function is used as it needs to PostTask() to itself on a different
// thread - which only works with a void return value.
CollectImpl(params, start_timestamp, std::move(profile));
CollectImpl(start_timestamp, std::move(profile));
}
void ChildCallStackProfileCollector::CollectImpl(
const CallStackProfileParams& params,
base::TimeTicks start_timestamp,
CallStackProfile profile) {
SampledProfile profile) {
base::AutoLock alock(lock_);
if (task_runner_ &&
// The profiler thread does not have a task runner. Attempting to
......@@ -90,16 +82,15 @@ void ChildCallStackProfileCollector::CollectImpl(
task_runner_->PostTask(
FROM_HERE, base::BindOnce(&ChildCallStackProfileCollector::CollectImpl,
// This class has lazy instance lifetime.
base::Unretained(this), params,
start_timestamp, std::move(profile)));
base::Unretained(this), start_timestamp,
std::move(profile)));
return;
}
if (parent_collector_) {
parent_collector_->Collect(params, start_timestamp, std::move(profile));
parent_collector_->Collect(start_timestamp, std::move(profile));
} else if (retain_profiles_) {
profiles_.push_back(
ProfileState(params, start_timestamp, std::move(profile)));
profiles_.push_back(ProfileState(start_timestamp, std::move(profile)));
}
}
......
......@@ -13,6 +13,7 @@
#include "base/synchronization/lock.h"
#include "components/metrics/call_stack_profile_builder.h"
#include "components/metrics/public/interfaces/call_stack_profile_collector.mojom.h"
#include "third_party/metrics_proto/sampled_profile.pb.h"
namespace service_manager {
class InterfaceProvider;
......@@ -56,7 +57,6 @@ class ChildCallStackProfileCollector {
// to the CallStackProfileBuilder, and should not be reused between
// CallStackProfileBuilders. This function may be called on any thread.
CallStackProfileBuilder::CompletedCallback GetProfilerCallback(
const CallStackProfileParams& params,
base::TimeTicks profile_start_time);
// Sets the CallStackProfileCollector interface from |parent_collector|. This
......@@ -74,32 +74,23 @@ class ChildCallStackProfileCollector {
struct ProfileState {
ProfileState();
ProfileState(ProfileState&&);
ProfileState(const CallStackProfileParams& params,
base::TimeTicks start_timestamp,
base::StackSamplingProfiler::CallStackProfile profile);
ProfileState(base::TimeTicks start_timestamp, SampledProfile profile);
~ProfileState();
ProfileState& operator=(ProfileState&&);
CallStackProfileParams params;
base::TimeTicks start_timestamp;
// The sampled profile.
base::StackSamplingProfiler::CallStackProfile profile;
SampledProfile profile;
private:
DISALLOW_COPY_AND_ASSIGN(ProfileState);
};
using CallStackProfile = base::StackSamplingProfiler::CallStackProfile;
void Collect(base::TimeTicks start_timestamp, SampledProfile profile);
void Collect(const CallStackProfileParams& params,
base::TimeTicks start_timestamp,
CallStackProfile profile);
void CollectImpl(const CallStackProfileParams& params,
base::TimeTicks start_timestamp,
CallStackProfile profile);
void CollectImpl(base::TimeTicks start_timestamp, SampledProfile profile);
// This object may be accessed on any thread, including the profiler
// thread. The expected use case for the object is to be created and have
......
......@@ -11,10 +11,7 @@
#include "base/bind.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "components/metrics/call_stack_profile_params.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace metrics {
......@@ -23,17 +20,14 @@ class ChildCallStackProfileCollectorTest : public testing::Test {
protected:
class Receiver : public mojom::CallStackProfileCollector {
public:
using CallStackProfile = base::StackSamplingProfiler::CallStackProfile;
explicit Receiver(mojom::CallStackProfileCollectorRequest request)
: binding_(this, std::move(request)) {}
~Receiver() override {}
void Collect(const CallStackProfileParams& params,
base::TimeTicks start_timestamp,
CallStackProfile profile) override {
void Collect(base::TimeTicks start_timestamp,
SampledProfile profile) override {
this->profiles.push_back(ChildCallStackProfileCollector::ProfileState(
params, start_timestamp, std::move(profile)));
start_timestamp, std::move(profile)));
}
std::vector<ChildCallStackProfileCollector::ProfileState> profiles;
......@@ -47,10 +41,9 @@ class ChildCallStackProfileCollectorTest : public testing::Test {
ChildCallStackProfileCollectorTest()
: receiver_impl_(new Receiver(MakeRequest(&receiver_))) {}
void CollectEmptyProfile(const CallStackProfileParams& params) {
base::StackSamplingProfiler::CallStackProfile profile;
child_collector_.GetProfilerCallback(params, base::TimeTicks::Now())
.Run(std::move(profile));
void CollectEmptyProfile() {
child_collector_.GetProfilerCallback(base::TimeTicks::Now())
.Run(SampledProfile());
}
const std::vector<ChildCallStackProfileCollector::ProfileState>& profiles()
......@@ -72,17 +65,8 @@ TEST_F(ChildCallStackProfileCollectorTest, InterfaceProvided) {
EXPECT_EQ(0u, profiles().size());
// Add a profile before providing the interface.
CollectEmptyProfile(CallStackProfileParams(
CallStackProfileParams::BROWSER_PROCESS,
CallStackProfileParams::MAIN_THREAD, CallStackProfileParams::JANKY_TASK,
CallStackProfileParams::PRESERVE_ORDER));
CollectEmptyProfile();
ASSERT_EQ(1u, profiles().size());
EXPECT_EQ(CallStackProfileParams::BROWSER_PROCESS,
profiles()[0].params.process);
EXPECT_EQ(CallStackProfileParams::MAIN_THREAD, profiles()[0].params.thread);
EXPECT_EQ(CallStackProfileParams::JANKY_TASK, profiles()[0].params.trigger);
EXPECT_EQ(CallStackProfileParams::PRESERVE_ORDER,
profiles()[0].params.ordering_spec);
base::TimeTicks start_timestamp = profiles()[0].start_timestamp;
EXPECT_GE(base::TimeDelta::FromMilliseconds(10),
base::TimeTicks::Now() - start_timestamp);
......@@ -92,29 +76,14 @@ TEST_F(ChildCallStackProfileCollectorTest, InterfaceProvided) {
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0u, profiles().size());
ASSERT_EQ(1u, receiver_impl_->profiles.size());
EXPECT_EQ(CallStackProfileParams::JANKY_TASK,
receiver_impl_->profiles[0].params.trigger);
EXPECT_EQ(CallStackProfileParams::PRESERVE_ORDER,
receiver_impl_->profiles[0].params.ordering_spec);
EXPECT_EQ(start_timestamp, receiver_impl_->profiles[0].start_timestamp);
// Add a profile after providing the interface. It should also be passed.
receiver_impl_->profiles.clear();
CollectEmptyProfile(CallStackProfileParams(
CallStackProfileParams::GPU_PROCESS, CallStackProfileParams::MAIN_THREAD,
CallStackProfileParams::THREAD_HUNG,
CallStackProfileParams::PRESERVE_ORDER));
CollectEmptyProfile();
base::RunLoop().RunUntilIdle();
EXPECT_EQ(0u, profiles().size());
ASSERT_EQ(1u, receiver_impl_->profiles.size());
EXPECT_EQ(CallStackProfileParams::GPU_PROCESS,
receiver_impl_->profiles[0].params.process);
EXPECT_EQ(CallStackProfileParams::MAIN_THREAD,
receiver_impl_->profiles[0].params.thread);
EXPECT_EQ(CallStackProfileParams::THREAD_HUNG,
receiver_impl_->profiles[0].params.trigger);
EXPECT_EQ(CallStackProfileParams::PRESERVE_ORDER,
receiver_impl_->profiles[0].params.ordering_spec);
EXPECT_GE(base::TimeDelta::FromMilliseconds(10),
(base::TimeTicks::Now() -
receiver_impl_->profiles[0].start_timestamp));
......@@ -124,17 +93,8 @@ TEST_F(ChildCallStackProfileCollectorTest, InterfaceNotProvided) {
EXPECT_EQ(0u, profiles().size());
// Add a profile before providing a null interface.
CollectEmptyProfile(CallStackProfileParams(
CallStackProfileParams::BROWSER_PROCESS,
CallStackProfileParams::MAIN_THREAD, CallStackProfileParams::JANKY_TASK,
CallStackProfileParams::PRESERVE_ORDER));
CollectEmptyProfile();
ASSERT_EQ(1u, profiles().size());
EXPECT_EQ(CallStackProfileParams::BROWSER_PROCESS,
profiles()[0].params.process);
EXPECT_EQ(CallStackProfileParams::MAIN_THREAD, profiles()[0].params.thread);
EXPECT_EQ(CallStackProfileParams::JANKY_TASK, profiles()[0].params.trigger);
EXPECT_EQ(CallStackProfileParams::PRESERVE_ORDER,
profiles()[0].params.ordering_spec);
EXPECT_GE(base::TimeDelta::FromMilliseconds(10),
base::TimeTicks::Now() - profiles()[0].start_timestamp);
......@@ -146,10 +106,7 @@ TEST_F(ChildCallStackProfileCollectorTest, InterfaceNotProvided) {
// Add a profile after providing a null interface. They should also be
// flushed.
CollectEmptyProfile(CallStackProfileParams(
CallStackProfileParams::GPU_PROCESS, CallStackProfileParams::MAIN_THREAD,
CallStackProfileParams::THREAD_HUNG,
CallStackProfileParams::PRESERVE_ORDER));
CollectEmptyProfile();
EXPECT_EQ(0u, profiles().size());
}
......
......@@ -15,5 +15,6 @@ source_set("call_stack_unit_tests") {
"//components/metrics/public/interfaces:call_stack_mojo_test_bindings",
"//mojo/public/cpp/bindings",
"//testing/gtest",
"//third_party/metrics_proto",
]
}
......@@ -4,24 +4,11 @@
mojom =
"//components/metrics/public/interfaces/call_stack_profile_collector.mojom"
public_headers = [
"//base/profiler/stack_sampling_profiler.h",
"//components/metrics/call_stack_profile_params.h",
]
public_headers = [ "//third_party/metrics_proto/sampled_profile.pb.h" ]
traits_headers =
[ "//components/metrics/public/cpp/call_stack_profile_struct_traits.h" ]
deps = [
"//base",
"//components/metrics:call_stack_profile",
]
type_mappings = [
"metrics.mojom.CallStackModule=base::StackSamplingProfiler::Module",
"metrics.mojom.CallStackFrame=base::StackSamplingProfiler::Frame",
"metrics.mojom.CallStackSample=base::StackSamplingProfiler::Sample[move_only]",
"metrics.mojom.CallStackProfile=base::StackSamplingProfiler::CallStackProfile[move_only]",
"metrics.mojom.CallStackProfileParams=metrics::CallStackProfileParams",
"metrics.mojom.Process=metrics::CallStackProfileParams::Process",
"metrics.mojom.SampleOrderingSpec=metrics::CallStackProfileParams::SampleOrderingSpec",
"metrics.mojom.Thread=metrics::CallStackProfileParams::Thread",
"metrics.mojom.Trigger=metrics::CallStackProfileParams::Trigger",
"//third_party/metrics_proto",
]
type_mappings =
[ "metrics.mojom.SampledProfile=metrics::SampledProfile[move_only]" ]
# Copyright 2018 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 is necessary because without it compiling
# call_stack_profile_struct_traits_unittest produces error below:
# "gen\third_party/metrics_proto/sampled_profile.pb.h(9,10): fatal error:
# 'google/protobuf/stubs/common.h' file not found".
mojom = "//components/metrics/public/interfaces/call_stack_profile_collector_test.mojom"
public_headers = [ "//third_party/metrics_proto/sampled_profile.pb.h" ]
traits_headers =
[ "//components/metrics/public/cpp/call_stack_profile_struct_traits.h" ]
deps = [
"//third_party/metrics_proto",
]
type_mappings =
[ "metrics.mojom.SampledProfile=metrics::SampledProfile[move_only]" ]
......@@ -2,4 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
typemaps = [ "//components/metrics/public/cpp/call_stack_profile.typemap" ]
typemaps = [
"//components/metrics/public/cpp/call_stack_profile.typemap",
"//components/metrics/public/cpp/call_stack_profile_unittest.typemap",
]
......@@ -4,75 +4,18 @@
module metrics.mojom;
import "mojo/public/mojom/base/file_path.mojom";
import "mojo/public/mojom/base/time.mojom";
// These structs mirror the corresponding types in base::StackSamplingProfiler.
struct CallStackModule {
uint64 base_address;
string id;
mojo_base.mojom.FilePath filename;
};
struct CallStackFrame {
uint64 instruction_pointer;
uint64 module_index;
};
struct CallStackSample {
array<CallStackFrame> frames;
uint32 process_milestones;
};
struct CallStackProfile {
array<CallStackModule> modules;
array<CallStackSample> samples;
mojo_base.mojom.TimeDelta profile_duration;
mojo_base.mojom.TimeDelta sampling_period;
};
enum Process {
UNKNOWN_PROCESS,
BROWSER_PROCESS,
RENDERER_PROCESS,
GPU_PROCESS,
UTILITY_PROCESS,
ZYGOTE_PROCESS,
SANDBOX_HELPER_PROCESS,
PPAPI_PLUGIN_PROCESS,
PPAPI_BROKER_PROCESS,
};
enum Thread {
UNKNOWN_THREAD,
MAIN_THREAD,
IO_THREAD,
COMPOSITOR_THREAD,
};
enum Trigger {
UNKNOWN,
PROCESS_STARTUP,
JANKY_TASK,
THREAD_HUNG,
PERIODIC_COLLECTION,
};
enum SampleOrderingSpec {
MAY_SHUFFLE,
PRESERVE_ORDER,
};
struct CallStackProfileParams {
Process process;
Thread thread;
Trigger trigger;
SampleOrderingSpec ordering_spec;
// |contents| is a serialized protobuf from
// src/third_party/metrics_proto/sampled_profile.proto.
//
// We pass this state via serialized protobuf because that is the ultimate
// metrics upload format.
struct SampledProfile {
string contents;
};
interface CallStackProfileCollector {
Collect(CallStackProfileParams params,
mojo_base.mojom.TimeTicks start_timestamp,
CallStackProfile profile);
Collect(mojo_base.mojom.TimeTicks start_timestamp,
SampledProfile profile);
};
......@@ -8,26 +8,5 @@ import "components/metrics/public/interfaces/call_stack_profile_collector.mojom"
interface CallStackProfileCollectorTest {
[Sync]
BounceFrame(CallStackFrame in) => (CallStackFrame out);
[Sync]
BounceModule(CallStackModule in) => (CallStackModule out);
[Sync]
BounceProfile(CallStackProfile in) => (CallStackProfile out);
[Sync]
BounceProcess(Process in) => (Process out);
[Sync]
BounceThread(Thread in) => (Thread out);
[Sync]
BounceTrigger(Trigger in) => (Trigger out);
[Sync]
BounceSampleOrderingSpec(SampleOrderingSpec in) => (SampleOrderingSpec out);
[Sync]
BounceCallStackProfileParams(CallStackProfileParams in) => (CallStackProfileParams out);
BounceSampledProfile(SampledProfile in) => (SampledProfile out);
};
......@@ -23,7 +23,6 @@
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "components/metrics/call_stack_profile_builder.h"
#include "components/metrics/call_stack_profile_metrics_provider.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/startup_metric_utils/browser/pref_names.h"
......@@ -577,7 +576,7 @@ void RecordBrowserMainMessageLoopStart(base::TimeTicks ticks,
// Record timing of the browser message-loop start time.
metrics::CallStackProfileBuilder::SetProcessMilestone(
metrics::CallStackProfileMetricsProvider::MAIN_LOOP_START);
metrics::CallStackProfileBuilder::MAIN_LOOP_START);
if (!is_first_run && !g_process_creation_ticks.is_null()) {
UMA_HISTOGRAM_AND_TRACE_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT(
UMA_HISTOGRAM_LONG_TIMES_100, "Startup.BrowserMessageLoopStartTime",
......@@ -684,7 +683,7 @@ void RecordFirstWebContentsNonEmptyPaint(
return;
metrics::CallStackProfileBuilder::SetProcessMilestone(
metrics::CallStackProfileMetricsProvider::FIRST_NONEMPTY_PAINT);
metrics::CallStackProfileBuilder::FIRST_NONEMPTY_PAINT);
UMA_HISTOGRAM_AND_TRACE_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT(
UMA_HISTOGRAM_LONG_TIMES_100, "Startup.FirstWebContents.NonEmptyPaint2",
g_process_creation_ticks, now);
......@@ -709,7 +708,7 @@ void RecordFirstWebContentsMainNavigationStart(base::TimeTicks ticks,
return;
metrics::CallStackProfileBuilder::SetProcessMilestone(
metrics::CallStackProfileMetricsProvider::MAIN_NAVIGATION_START);
metrics::CallStackProfileBuilder::MAIN_NAVIGATION_START);
UMA_HISTOGRAM_AND_TRACE_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT(
UMA_HISTOGRAM_LONG_TIMES_100,
"Startup.FirstWebContents.MainNavigationStart", g_process_creation_ticks,
......@@ -740,7 +739,7 @@ void RecordFirstWebContentsMainNavigationFinished(base::TimeTicks ticks) {
return;
metrics::CallStackProfileBuilder::SetProcessMilestone(
metrics::CallStackProfileMetricsProvider::MAIN_NAVIGATION_FINISHED);
metrics::CallStackProfileBuilder::MAIN_NAVIGATION_FINISHED);
UMA_HISTOGRAM_AND_TRACE_WITH_TEMPERATURE_AND_SAME_VERSION_COUNT(
UMA_HISTOGRAM_LONG_TIMES_100,
"Startup.FirstWebContents.MainNavigationFinished",
......
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