-// Copyright 2018 The Chromium Authors. All rights reserved.
+// Copyright 2018 The Chromium Authors
// 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_builder.h"
+#include <stdint.h>
#include <algorithm>
#include <iterator>
#include <map>
+#include <memory>
#include <string>
#include <tuple>
#include <utility>
+#include "base/check.h"
#include "base/files/file_path.h"
-#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/metrics/metrics_hashes.h"
#include "base/no_destructor.h"
-#include "base/stl_util.h"
+#include "base/numerics/safe_conversions.h"
+#include "base/time/time.h"
#include "build/build_config.h"
#include "components/metrics/call_stack_profile_encoding.h"
namespace {
-// Only used by child processes.
-base::LazyInstance<ChildCallStackProfileCollector>::Leaky
- g_child_call_stack_profile_collector = LAZY_INSTANCE_INITIALIZER;
+// Only used by child processes. This returns a unique_ptr so that it can be
+// reset during tests.
+std::unique_ptr<ChildCallStackProfileCollector>&
+GetChildCallStackProfileCollector() {
+ static base::NoDestructor<std::unique_ptr<ChildCallStackProfileCollector>>
+ instance(std::make_unique<ChildCallStackProfileCollector>());
+ return *instance;
+}
base::RepeatingCallback<void(base::TimeTicks, SampledProfile)>&
GetBrowserProcessReceiverCallbackInstance() {
sampled_profile_.set_thread(ToExecutionContextThread(profile_params.thread));
sampled_profile_.set_trigger_event(
ToSampledProfileTriggerEvent(profile_params.trigger));
+ if (!profile_params.time_offset.is_zero()) {
+ DCHECK(profile_params.time_offset.is_positive());
+ CallStackProfile* call_stack_profile =
+ sampled_profile_.mutable_call_stack_profile();
+ call_stack_profile->set_profile_time_offset_ms(
+ profile_params.time_offset.InMilliseconds());
+ }
}
CallStackProfileBuilder::~CallStackProfileBuilder() = default;
base::TimeTicks period_start,
base::TimeTicks period_end,
const base::MetadataRecorder::Item& item) {
- DCHECK_LE(period_start, period_end);
- DCHECK_LE(period_end, base::TimeTicks::Now());
+ CHECK_LE(period_start, period_end);
+ CHECK_LE(period_end, base::TimeTicks::Now());
// We don't set metadata if the period extends before the start of the
// sampling, to avoid biasing against the unobserved execution. This will
google::protobuf::RepeatedPtrField<CallStackProfile::StackSample>* samples =
call_stack_profile->mutable_stack_sample();
- DCHECK_EQ(sample_timestamps_.size(), static_cast<size_t>(samples->size()));
+ CHECK_EQ(sample_timestamps_.size(), static_cast<size_t>(samples->size()));
const ptrdiff_t start_offset =
std::lower_bound(sample_timestamps_.begin(), sample_timestamps_.end(),
call_stack_profile->mutable_metadata_name_hash());
}
+void CallStackProfileBuilder::AddProfileMetadata(
+ const base::MetadataRecorder::Item& item) {
+ CallStackProfile* call_stack_profile =
+ sampled_profile_.mutable_call_stack_profile();
+
+ metadata_.SetMetadata(item,
+ call_stack_profile->mutable_profile_metadata()->Add(),
+ call_stack_profile->mutable_metadata_name_hash());
+}
+
void CallStackProfileBuilder::OnSampleCompleted(
std::vector<base::Frame> frames,
base::TimeTicks sample_timestamp) {
CallStackProfile::Stack stack;
for (const auto& frame : frames) {
+ // The function name should never be provided in UMA profiler usage.
+ DCHECK(frame.function_name.empty());
+
// keep the frame information even if its module is invalid so we have
// visibility into how often this issue is happening on the server.
CallStackProfile::Location* location = stack.add_frame();
// Write CallStackProfile::Location protobuf message.
uintptr_t instruction_pointer = frame.instruction_pointer;
-#if defined(OS_IOS)
+#if BUILDFLAG(IS_IOS)
#if !TARGET_IPHONE_SIMULATOR
// Some iOS devices enable pointer authentication, which uses the
// higher-order bits of pointers to store a signature. Strip that signature
// available.
instruction_pointer &= 0xFFFFFFFFF;
#endif // !TARGET_IPHONE_SIMULATOR
-#endif // defined(OS_IOS)
+#endif // BUILDFLAG(IS_IOS)
ptrdiff_t module_offset =
reinterpret_cast<const char*>(instruction_pointer) -
if (profile_start_time_.is_null())
profile_start_time_ = sample_timestamp;
+ // Write timestamps to protobuf message. Currently the timestamps are only
+ // used for browser process to apply LCP tags. The browser process will clear
+ // the timestamps information once it is done with LCP tagging. Timestamps
+ // will not be included in the final profile sent to UMA.
+ const int64_t offset =
+ (sample_timestamp - profile_start_time_).InMilliseconds();
+ stack_sample_proto->set_sample_time_offset_ms(
+ base::saturated_cast<int32_t>(offset));
+
sample_timestamps_.push_back(sample_timestamp);
}
profile_duration.InMilliseconds());
call_stack_profile->set_sampling_period_ms(sampling_period.InMilliseconds());
+ // Heap profiler sets `profile_time_offset_ms` through constructor.
+ // For CPU profiles, `profile_time_offset_ms` is the time of the first
+ // sample.
+ if (!call_stack_profile->has_profile_time_offset_ms()) {
+ call_stack_profile->set_profile_time_offset_ms(
+ profile_start_time_.since_origin().InMilliseconds());
+ }
+
// Write CallStackProfile::ModuleIdentifier protobuf message.
for (const auto* module : modules_) {
CallStackProfile::ModuleIdentifier* module_id =
module_id->set_name_md5_prefix(
HashModuleFilename(module->GetDebugBasename()));
}
+ // sampled_profile_ cannot be reused after it is cleared by this function.
+ // Check we still have the information from the constructor.
+ CHECK(sampled_profile_.has_process());
+ CHECK(sampled_profile_.has_thread());
+ CHECK(sampled_profile_.has_trigger_event());
PassProfilesToMetricsProvider(profile_start_time_,
std::move(sampled_profile_));
+ // Protobuffers are in an uncertain state after moving from; clear to get
+ // back to known state.
+ sampled_profile_.Clear();
// Run the completed callback if there is one.
if (!completed_callback_.is_null())
stack_index_.clear();
module_index_.clear();
modules_.clear();
+ sample_timestamps_.clear();
}
// static
void CallStackProfileBuilder::SetParentProfileCollectorForChildProcess(
mojo::PendingRemote<metrics::mojom::CallStackProfileCollector>
browser_interface) {
- g_child_call_stack_profile_collector.Get().SetParentProfileCollector(
+ GetChildCallStackProfileCollector()->SetParentProfileCollector(
std::move(browser_interface));
}
+// static
+void CallStackProfileBuilder::ResetChildCallStackProfileCollectorForTesting() {
+ GetChildCallStackProfileCollector() =
+ std::make_unique<ChildCallStackProfileCollector>();
+}
+
void CallStackProfileBuilder::PassProfilesToMetricsProvider(
base::TimeTicks profile_start_time,
SampledProfile sampled_profile) {
GetBrowserProcessReceiverCallbackInstance().Run(profile_start_time,
std::move(sampled_profile));
} else {
- g_child_call_stack_profile_collector.Get()
- .ChildCallStackProfileCollector::Collect(profile_start_time,
+ GetChildCallStackProfileCollector()->Collect(profile_start_time,
std::move(sampled_profile));
}
}