Commit 01fed52e authored by Oystein Eftevaag's avatar Oystein Eftevaag Committed by Commit Bot

Add browsertest for the tracing sampler profiler output

This verifies that the JSON which gets produced with the sampled
frames, matches the format that the internal processing pipelines
expect.

R=ssid@chromium.org,etienneb@chromium.org
BUG=1046918

Change-Id: Iaa4e853fe44f8ea16ac81410383413c55cbfbd6a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2031941
Commit-Queue: oysteine <oysteine@chromium.org>
Reviewed-by: default avatarssid <ssid@chromium.org>
Cr-Commit-Position: refs/heads/master@{#738675}
parent 67674189
......@@ -727,10 +727,23 @@ bool ParseEventsFromJson(const std::string& json,
std::vector<TraceEvent>* output) {
base::Optional<base::Value> root = base::JSONReader::Read(json);
if (!root || !root->is_list())
if (!root)
return false;
for (const auto& item : root->GetList()) {
base::Value::ListView list;
if (root->is_list()) {
list = root->GetList();
} else if (root->is_dict()) {
base::Value* trace_events = root->FindListKey("traceEvents");
if (!trace_events)
return false;
list = trace_events->GetList();
} else {
return false;
}
for (const auto& item : list) {
TraceEvent event;
if (!event.SetFromJSON(&item))
return false;
......
......@@ -16,11 +16,15 @@
#include "base/metrics/histogram_macros.h"
#include "base/process/process_handle.h"
#include "base/run_loop.h"
#include "base/sampling_heap_profiler/module_cache.h"
#include "base/strings/pattern.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_tokenizer.h"
#include "base/task/post_task.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_timeouts.h"
#include "base/test/trace_event_analyzer.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "content/browser/devtools/protocol/devtools_protocol_test_support.h"
......@@ -35,7 +39,9 @@
#include "content/public/test/content_browser_test_utils.h"
#include "content/public/test/test_utils.h"
#include "services/tracing/perfetto/privacy_filtering_check.h"
#include "services/tracing/public/cpp/stack_sampling/tracing_sampler_profiler.h"
#include "services/tracing/public/cpp/tracing_features.h"
#include "third_party/re2/src/re2/re2.h"
#include "third_party/zlib/zlib.h"
#if defined(OS_POSIX)
......@@ -1087,6 +1093,119 @@ IN_PROC_BROWSER_TEST_F(BackgroundTracingManagerBrowserTest, CustomConfig) {
<< *trace_config;
}
// Used as a known symbol to look up the current module.
void DummyFunc() {}
// Test that the tracing sampler profiler running in background tracing mode,
// produces stack frames in the expected JSON format.
IN_PROC_BROWSER_TEST_F(BackgroundTracingManagerBrowserTest,
EndToEndStackSampling) {
// In the browser process, the tracing sampler profiler gets constructed by
// the chrome/ layer, so we need to do the same manually for testing purposes.
auto tracing_sampler_profiler =
tracing::TracingSamplerProfiler::CreateOnMainThread();
// There won't be any samples if stack unwinding isn't supported.
if (!tracing::TracingSamplerProfiler::IsStackUnwindingSupported()) {
return;
}
base::RunLoop wait_for_sample;
tracing_sampler_profiler->SetSampleCallbackForTesting(
wait_for_sample.QuitClosure());
TestBackgroundTracingHelper background_tracing_helper;
TestTraceReceiverHelper trace_receiver_helper;
base::DictionaryValue dict;
dict.SetString("mode", "PREEMPTIVE_TRACING_MODE");
dict.SetString("category", "CUSTOM");
dict.SetString("custom_categories", "disabled-by-default-cpu_profiler,-*");
std::unique_ptr<base::ListValue> rules_list(new base::ListValue());
{
std::unique_ptr<base::DictionaryValue> rules_dict(
new base::DictionaryValue());
rules_dict->SetString("rule", "MONITOR_AND_DUMP_WHEN_TRIGGER_NAMED");
rules_dict->SetString("trigger_name", "preemptive_test");
rules_list->Append(std::move(rules_dict));
}
dict.Set("configs", std::move(rules_list));
std::unique_ptr<BackgroundTracingConfig> config(
BackgroundTracingConfigImpl::FromDict(&dict));
EXPECT_TRUE(config);
content::BackgroundTracingManager::TriggerHandle handle =
content::BackgroundTracingManager::GetInstance()->RegisterTriggerType(
"preemptive_test");
EXPECT_TRUE(BackgroundTracingManager::GetInstance()->SetActiveScenario(
std::move(config), trace_receiver_helper.get_receive_callback(),
BackgroundTracingManager::ANONYMIZE_DATA));
background_tracing_helper.WaitForTracingEnabled();
wait_for_sample.Run();
TestTriggerHelper trigger_helper;
BackgroundTracingManager::GetInstance()->TriggerNamedEvent(
handle, trigger_helper.receive_closure(true));
trace_receiver_helper.WaitForTraceReceived();
BackgroundTracingManager::GetInstance()->AbortScenarioForTesting();
background_tracing_helper.WaitForScenarioAborted();
EXPECT_TRUE(trace_receiver_helper.trace_received());
trace_analyzer::TraceEventVector events;
std::unique_ptr<trace_analyzer::TraceAnalyzer> analyzer(
trace_analyzer::TraceAnalyzer::Create(
trace_receiver_helper.file_contents()));
ASSERT_TRUE(analyzer);
base::ModuleCache module_cache;
const base::ModuleCache::Module* this_module =
module_cache.GetModuleForAddress(reinterpret_cast<uintptr_t>(&DummyFunc));
ASSERT_TRUE(this_module);
std::string module_id = this_module->GetId();
tracing::TracingSamplerProfiler::MangleModuleIDIfNeeded(&module_id);
std::string desired_frame_pattern = base::StrCat(
{"0x[[:xdigit:]]+ - /?", this_module->GetDebugBasename().MaybeAsASCII(),
" \\[", module_id, "\\]"});
analyzer->FindEvents(trace_analyzer::Query::EventName() ==
trace_analyzer::Query::String("StackCpuSampling"),
&events);
EXPECT_GT(events.size(), 0u);
bool found_match = false;
for (const trace_analyzer::TraceEvent* event : events) {
if (found_match) {
break;
}
std::string frames = event->GetKnownArgAsString("frames");
EXPECT_FALSE(frames.empty());
base::StringTokenizer values_tokenizer(frames, "\n");
while (values_tokenizer.GetNext()) {
if (values_tokenizer.token_is_delim()) {
continue;
}
if (RE2::FullMatch(values_tokenizer.token(), desired_frame_pattern)) {
found_match = true;
break;
}
}
}
EXPECT_TRUE(found_match);
}
// This tests that histogram triggers for reactive mode configs.
IN_PROC_BROWSER_TEST_F(BackgroundTracingManagerBrowserTest,
ReceiveReactiveTraceSucceedsOnHigherHistogramSample) {
......
......@@ -1165,6 +1165,7 @@ test("content_browsertests") {
"//third_party/blink/public/mojom/frame",
"//third_party/leveldatabase",
"//third_party/mesa_headers",
"//third_party/re2",
"//third_party/zlib",
"//ui/accessibility",
"//ui/accessibility:ax_enums_mojo",
......
......@@ -183,10 +183,12 @@ TracingSamplerProfiler::TracingProfileBuilder::BufferedSample::BufferedSample(
TracingSamplerProfiler::TracingProfileBuilder::TracingProfileBuilder(
base::PlatformThreadId sampled_thread_id,
std::unique_ptr<perfetto::TraceWriter> trace_writer,
bool should_enable_filtering)
bool should_enable_filtering,
const base::RepeatingClosure& sample_callback_for_testing)
: sampled_thread_id_(sampled_thread_id),
trace_writer_(std::move(trace_writer)),
should_enable_filtering_(should_enable_filtering) {}
should_enable_filtering_(should_enable_filtering),
sample_callback_for_testing_(sample_callback_for_testing) {}
TracingSamplerProfiler::TracingProfileBuilder::~TracingProfileBuilder() {
// Deleting a TraceWriter can end up triggering a Mojo call which calls
......@@ -227,6 +229,10 @@ void TracingSamplerProfiler::TracingProfileBuilder::OnSampleCompleted(
buffered_samples_.clear();
}
WriteSampleToTrace(BufferedSample(sample_timestamp, std::move(frames)));
if (sample_callback_for_testing_) {
sample_callback_for_testing_.Run();
}
}
void TracingSamplerProfiler::TracingProfileBuilder::WriteSampleToTrace(
......@@ -362,21 +368,7 @@ TracingSamplerProfiler::TracingProfileBuilder::GetCallstackIDAndMaybeEmit(
}
#endif
#if defined(OS_ANDROID) || defined(OS_LINUX)
// Linux ELF module IDs are 160bit integers, which we need to mangle
// down to 128bit integers to match the id that Breakpad outputs.
// Example on version '66.0.3359.170' x64:
// Build-ID: "7f0715c2 86f8 b16c 10e4ad349cda3b9b 56c7a773
// Debug-ID "C215077F F886 6CB1 10E4AD349CDA3B9B 0"
if (module_id.size() >= 32) {
module_id =
base::StrCat({module_id.substr(6, 2), module_id.substr(4, 2),
module_id.substr(2, 2), module_id.substr(0, 2),
module_id.substr(10, 2), module_id.substr(8, 2),
module_id.substr(14, 2), module_id.substr(12, 2),
module_id.substr(16, 16), "0"});
}
#endif
MangleModuleIDIfNeeded(&module_id);
// We never emit frame names in privacy filtered mode.
bool should_emit_frame_names =
......@@ -462,6 +454,25 @@ TracingSamplerProfiler::TracingProfileBuilder::GetCallstackIDAndMaybeEmit(
return interned_callstack.id;
}
// static
void TracingSamplerProfiler::MangleModuleIDIfNeeded(std::string* module_id) {
#if defined(OS_ANDROID) || defined(OS_LINUX)
// Linux ELF module IDs are 160bit integers, which we need to mangle
// down to 128bit integers to match the id that Breakpad outputs.
// Example on version '66.0.3359.170' x64:
// Build-ID: "7f0715c2 86f8 b16c 10e4ad349cda3b9b 56c7a773
// Debug-ID "C215077F F886 6CB1 10E4AD349CDA3B9B 0"
if (module_id->size() >= 32) {
*module_id =
base::StrCat({module_id->substr(6, 2), module_id->substr(4, 2),
module_id->substr(2, 2), module_id->substr(0, 2),
module_id->substr(10, 2), module_id->substr(8, 2),
module_id->substr(14, 2), module_id->substr(12, 2),
module_id->substr(16, 16), "0"});
}
#endif
}
// static
std::unique_ptr<TracingSamplerProfiler>
TracingSamplerProfiler::CreateOnMainThread() {
......@@ -518,6 +529,12 @@ TracingSamplerProfiler::~TracingSamplerProfiler() {
TracingSamplerProfilerDataSource::Get()->UnregisterProfiler(this);
}
void TracingSamplerProfiler::SetSampleCallbackForTesting(
const base::RepeatingClosure& sample_callback_for_testing) {
base::AutoLock lock(lock_);
sample_callback_for_testing_ = sample_callback_for_testing;
}
void TracingSamplerProfiler::StartTracing(
std::unique_ptr<perfetto::TraceWriter> trace_writer,
bool should_enable_filtering) {
......@@ -546,7 +563,7 @@ void TracingSamplerProfiler::StartTracing(
auto profile_builder = std::make_unique<TracingProfileBuilder>(
sampled_thread_token_.id, std::move(trace_writer),
should_enable_filtering);
should_enable_filtering, sample_callback_for_testing_);
profile_builder_ = profile_builder.get();
// Create and start the stack sampling profiler.
#if defined(OS_ANDROID)
......
......@@ -10,13 +10,16 @@
#include <utility>
#include <vector>
#include "base/callback.h"
#include "base/component_export.h"
#include "base/debug/debugging_buildflags.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "base/profiler/sampling_profiler_thread_token.h"
#include "base/profiler/stack_sampling_profiler.h"
#include "base/sequence_checker.h"
#include "base/threading/platform_thread.h"
#include "build/build_config.h"
#include "services/tracing/public/cpp/perfetto/interning_index.h"
#include "third_party/perfetto/include/perfetto/ext/tracing/core/trace_writer.h"
......@@ -40,9 +43,12 @@ class COMPONENT_EXPORT(TRACING_CPP) TracingSamplerProfiler {
class COMPONENT_EXPORT(TRACING_CPP) TracingProfileBuilder
: public base::ProfileBuilder {
public:
TracingProfileBuilder(base::PlatformThreadId sampled_thread_id,
std::unique_ptr<perfetto::TraceWriter> trace_writer,
bool should_enable_filtering);
TracingProfileBuilder(
base::PlatformThreadId sampled_thread_id,
std::unique_ptr<perfetto::TraceWriter> trace_writer,
bool should_enable_filtering,
const base::RepeatingClosure& sample_callback_for_testing =
base::RepeatingClosure());
~TracingProfileBuilder() override;
// base::ProfileBuilder
......@@ -97,6 +103,7 @@ class COMPONENT_EXPORT(TRACING_CPP) TracingSamplerProfiler {
int32_t last_emitted_process_priority_ = -1;
base::TimeTicks last_timestamp_;
const bool should_enable_filtering_;
base::RepeatingClosure sample_callback_for_testing_;
};
// Creates sampling profiler on main thread. The profiler *must* be
......@@ -117,11 +124,29 @@ class COMPONENT_EXPORT(TRACING_CPP) TracingSamplerProfiler {
static void DeleteOnChildThreadForTesting();
static void StartTracingForTesting(tracing::PerfettoProducer* producer);
static void StopTracingForTesting();
static void MangleModuleIDIfNeeded(std::string* module_id);
// Returns whether of not the sampler profiling is able to unwind the stack
// on this platform.
constexpr static bool IsStackUnwindingSupported() {
#if defined(OS_MACOSX) || defined(OS_WIN) && defined(_WIN64) || \
(defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
defined(OFFICIAL_BUILD))
return true;
#else
return false;
#endif
}
explicit TracingSamplerProfiler(
base::SamplingProfilerThreadToken sampled_thread_token);
virtual ~TracingSamplerProfiler();
// The given callback will be called for every received sample, and can be
// called on any thread. Must be called before tracing is started.
void SetSampleCallbackForTesting(
const base::RepeatingClosure& sample_callback_for_testing);
void StartTracing(std::unique_ptr<perfetto::TraceWriter> trace_writer,
bool should_enable_filtering);
void StopTracing();
......@@ -132,6 +157,7 @@ class COMPONENT_EXPORT(TRACING_CPP) TracingSamplerProfiler {
base::Lock lock_;
std::unique_ptr<base::StackSamplingProfiler> profiler_; // under |lock_|
TracingProfileBuilder* profile_builder_ = nullptr;
base::RepeatingClosure sample_callback_for_testing_;
DISALLOW_COPY_AND_ASSIGN(TracingSamplerProfiler);
};
......
......@@ -166,18 +166,6 @@ class TracingSampleProfilerTest : public testing::Test {
base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(200));
}
// Returns whether of not the sampler profiling is able to unwind the stack
// on this platform.
bool IsStackUnwindingSupported() {
#if defined(OS_MACOSX) || defined(OS_WIN) && defined(_WIN64) || \
(defined(OS_ANDROID) && BUILDFLAG(CAN_UNWIND_WITH_CFI_TABLE) && \
defined(OFFICIAL_BUILD))
return true;
#else
return false;
#endif
}
void EndTracing() {
TracingSamplerProfiler::StopTracingForTesting();
base::RunLoop().RunUntilIdle();
......@@ -191,7 +179,7 @@ class TracingSampleProfilerTest : public testing::Test {
}
void ValidateReceivedEvents() {
if (IsStackUnwindingSupported()) {
if (TracingSamplerProfiler::IsStackUnwindingSupported()) {
EXPECT_GT(events_stack_received_count_, 0U);
} else {
EXPECT_EQ(events_stack_received_count_, 0U);
......@@ -280,7 +268,7 @@ TEST_F(TracingSampleProfilerTest, TestStartupTracing) {
WaitForEvents();
EndTracing();
base::RunLoop().RunUntilIdle();
if (IsStackUnwindingSupported()) {
if (TracingSamplerProfiler::IsStackUnwindingSupported()) {
uint32_t seq_id = FindProfilerSequenceId();
auto& packets = producer()->finalized_packets();
int64_t reference_ts = 0;
......@@ -314,7 +302,7 @@ TEST_F(TracingSampleProfilerTest, JoinStartupTracing) {
WaitForEvents();
EndTracing();
base::RunLoop().RunUntilIdle();
if (IsStackUnwindingSupported()) {
if (TracingSamplerProfiler::IsStackUnwindingSupported()) {
uint32_t seq_id = FindProfilerSequenceId();
auto& packets = producer()->finalized_packets();
int64_t reference_ts = 0;
......
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