blob: d23842b251cdec93914a6b03c68b778fcf972282 [file] [log] [blame]
// 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.
}
}
}
// The sample order in |profile| is not preserved in |proto_profile|.
void CopyProfileToProto(
const base::StackSamplingProfiler::CallStackProfile& profile,
CallStackProfile* proto_profile) {
if (profile.samples.empty())
return;
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;
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;
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