Original Revision: https://phabricator.services.mozilla.com/D257403 Differential Revision: https://phabricator.services.mozilla.com/D258229
337 lines
13 KiB
C++
337 lines
13 KiB
C++
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
#include "GeckoProfiler.h"
|
|
#include "ProfilerStackWalk.h"
|
|
|
|
#include "mozilla/Maybe.h"
|
|
#include "nsPrintfCString.h"
|
|
#include "public/GeckoTraceEvent.h"
|
|
#include "mozilla/ProfilerState.h"
|
|
|
|
using namespace mozilla;
|
|
using webrtc::trace_event_internal::TraceValueUnion;
|
|
|
|
void uprofiler_register_thread(const char* name, void* stacktop) {
|
|
#ifdef MOZ_GECKO_PROFILER
|
|
profiler_register_thread(name, stacktop);
|
|
#endif // MOZ_GECKO_PROFILER
|
|
}
|
|
|
|
void uprofiler_unregister_thread() {
|
|
#ifdef MOZ_GECKO_PROFILER
|
|
profiler_unregister_thread();
|
|
#endif // MOZ_GECKO_PROFILER
|
|
}
|
|
|
|
#ifdef MOZ_GECKO_PROFILER
|
|
namespace {
|
|
Maybe<MarkerTiming> ToTiming(char phase) {
|
|
switch (phase) {
|
|
case 'B':
|
|
return Some(MarkerTiming::IntervalStart());
|
|
case 'E':
|
|
return Some(MarkerTiming::IntervalEnd());
|
|
case 'I':
|
|
return Some(MarkerTiming::InstantNow());
|
|
default:
|
|
return Nothing();
|
|
}
|
|
}
|
|
|
|
MarkerCategory ToCategory(const char category) {
|
|
switch (category) {
|
|
case 'S':
|
|
return geckoprofiler::category::SANDBOX;
|
|
case 'M':
|
|
return geckoprofiler::category::MEDIA_RT;
|
|
default:
|
|
return geckoprofiler::category::OTHER;
|
|
}
|
|
}
|
|
|
|
struct TraceOption {
|
|
bool mPassed = false;
|
|
ProfilerString8View mName;
|
|
Variant<int64_t, bool, double, ProfilerString8View> mValue = AsVariant(false);
|
|
};
|
|
|
|
struct TraceMarker {
|
|
static constexpr int MAX_NUM_ARGS = 6;
|
|
using OptionsType = std::tuple<TraceOption, TraceOption, TraceOption,
|
|
TraceOption, TraceOption, TraceOption>;
|
|
static constexpr mozilla::Span<const char> MarkerTypeName() {
|
|
return MakeStringSpan("TraceEvent");
|
|
}
|
|
static void StreamJSONMarkerData(
|
|
mozilla::baseprofiler::SpliceableJSONWriter& aWriter,
|
|
const OptionsType& aArgs) {
|
|
auto writeValue = [&](const auto& aName, const auto& aVariant) {
|
|
aVariant.match(
|
|
[&](const int64_t& aValue) { aWriter.IntProperty(aName, aValue); },
|
|
[&](const bool& aValue) { aWriter.BoolProperty(aName, aValue); },
|
|
[&](const double& aValue) { aWriter.DoubleProperty(aName, aValue); },
|
|
[&](const ProfilerString8View& aValue) {
|
|
aWriter.StringProperty(aName, aValue);
|
|
});
|
|
};
|
|
if (const auto& arg = std::get<0>(aArgs); arg.mPassed) {
|
|
aWriter.StringProperty("name1", arg.mName);
|
|
writeValue("val1", arg.mValue);
|
|
}
|
|
if (const auto& arg = std::get<1>(aArgs); arg.mPassed) {
|
|
aWriter.StringProperty("name2", arg.mName);
|
|
writeValue("val2", arg.mValue);
|
|
}
|
|
if (const auto& arg = std::get<2>(aArgs); arg.mPassed) {
|
|
aWriter.StringProperty("name3", arg.mName);
|
|
writeValue("val3", arg.mValue);
|
|
}
|
|
if (const auto& arg = std::get<3>(aArgs); arg.mPassed) {
|
|
aWriter.StringProperty("name4", arg.mName);
|
|
writeValue("val4", arg.mValue);
|
|
}
|
|
if (const auto& arg = std::get<4>(aArgs); arg.mPassed) {
|
|
aWriter.StringProperty("name5", arg.mName);
|
|
writeValue("val5", arg.mValue);
|
|
}
|
|
if (const auto& arg = std::get<5>(aArgs); arg.mPassed) {
|
|
aWriter.StringProperty("name6", arg.mName);
|
|
writeValue("val6", arg.mValue);
|
|
}
|
|
}
|
|
static mozilla::MarkerSchema MarkerTypeDisplay() {
|
|
using MS = MarkerSchema;
|
|
MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
|
|
schema.SetChartLabel("{marker.name}");
|
|
schema.SetTableLabel(
|
|
"{marker.name} {marker.data.name1} {marker.data.val1} "
|
|
"{marker.data.name2} {marker.data.val2}"
|
|
"{marker.data.name3} {marker.data.val3}"
|
|
"{marker.data.name4} {marker.data.val4}"
|
|
"{marker.data.name5} {marker.data.val5}"
|
|
"{marker.data.name6} {marker.data.val6}");
|
|
schema.AddKeyLabelFormatSearchable("name1", "Key 1", MS::Format::String,
|
|
MS::Searchable::Searchable);
|
|
schema.AddKeyLabelFormatSearchable("val1", "Value 1", MS::Format::String,
|
|
MS::Searchable::Searchable);
|
|
schema.AddKeyLabelFormatSearchable("name2", "Key 2", MS::Format::String,
|
|
MS::Searchable::Searchable);
|
|
schema.AddKeyLabelFormatSearchable("val2", "Value 2", MS::Format::String,
|
|
MS::Searchable::Searchable);
|
|
schema.AddKeyLabelFormatSearchable("name3", "Key 3", MS::Format::String,
|
|
MS::Searchable::Searchable);
|
|
schema.AddKeyLabelFormatSearchable("val3", "Value 3", MS::Format::String,
|
|
MS::Searchable::Searchable);
|
|
schema.AddKeyLabelFormatSearchable("name4", "Key 4", MS::Format::String,
|
|
MS::Searchable::Searchable);
|
|
schema.AddKeyLabelFormatSearchable("val4", "Value 4", MS::Format::String,
|
|
MS::Searchable::Searchable);
|
|
schema.AddKeyLabelFormatSearchable("name5", "Key 5", MS::Format::String,
|
|
MS::Searchable::Searchable);
|
|
schema.AddKeyLabelFormatSearchable("val5", "Value 5", MS::Format::String,
|
|
MS::Searchable::Searchable);
|
|
schema.AddKeyLabelFormatSearchable("name6", "Key 6", MS::Format::String,
|
|
MS::Searchable::Searchable);
|
|
schema.AddKeyLabelFormatSearchable("val6", "Value 6", MS::Format::String,
|
|
MS::Searchable::Searchable);
|
|
return schema;
|
|
}
|
|
};
|
|
} // namespace
|
|
|
|
namespace mozilla {
|
|
template <>
|
|
struct ProfileBufferEntryWriter::Serializer<TraceOption> {
|
|
static Length Bytes(const TraceOption& aOption) {
|
|
// 1 byte to store passed flag, then object size if passed.
|
|
return aOption.mPassed ? (1 + SumBytes(aOption.mName, aOption.mValue)) : 1;
|
|
}
|
|
|
|
static void Write(ProfileBufferEntryWriter& aEW, const TraceOption& aOption) {
|
|
// 'T'/'t' is just an arbitrary 1-byte value to distinguish states.
|
|
if (aOption.mPassed) {
|
|
aEW.WriteObject<char>('T');
|
|
// Use the Serializer for the name/value pair.
|
|
aEW.WriteObject(aOption.mName);
|
|
aEW.WriteObject(aOption.mValue);
|
|
} else {
|
|
aEW.WriteObject<char>('t');
|
|
}
|
|
}
|
|
};
|
|
|
|
template <>
|
|
struct ProfileBufferEntryReader::Deserializer<TraceOption> {
|
|
static void ReadInto(ProfileBufferEntryReader& aER, TraceOption& aOption) {
|
|
char c = aER.ReadObject<char>();
|
|
if ((aOption.mPassed = (c == 'T'))) {
|
|
aER.ReadIntoObject(aOption.mName);
|
|
aER.ReadIntoObject(aOption.mValue);
|
|
} else {
|
|
MOZ_ASSERT(c == 't');
|
|
}
|
|
}
|
|
|
|
static TraceOption Read(ProfileBufferEntryReader& aER) {
|
|
TraceOption option;
|
|
ReadInto(aER, option);
|
|
return option;
|
|
}
|
|
};
|
|
} // namespace mozilla
|
|
#endif // MOZ_GECKO_PROFILER
|
|
|
|
void uprofiler_simple_event_marker_internal(
|
|
const char* name, const char category, char phase, int num_args,
|
|
const char** arg_names, const unsigned char* arg_types,
|
|
const unsigned long long* arg_values, bool capture_stack = false,
|
|
void* provided_stack = nullptr) {
|
|
#ifdef MOZ_GECKO_PROFILER
|
|
if (!profiler_thread_is_being_profiled_for_markers()) {
|
|
return;
|
|
}
|
|
Maybe<MarkerTiming> timing = ToTiming(phase);
|
|
if (!timing) {
|
|
if (getenv("MOZ_LOG_UNKNOWN_TRACE_EVENT_PHASES")) {
|
|
fprintf(stderr, "XXX UProfiler: phase not handled: '%c'\n", phase);
|
|
}
|
|
return;
|
|
}
|
|
MOZ_ASSERT(num_args <= TraceMarker::MAX_NUM_ARGS);
|
|
TraceMarker::OptionsType tuple;
|
|
TraceOption* args[TraceMarker::MAX_NUM_ARGS] = {
|
|
&std::get<0>(tuple), &std::get<1>(tuple), &std::get<2>(tuple),
|
|
&std::get<3>(tuple), &std::get<4>(tuple), &std::get<5>(tuple)};
|
|
nsCString strValueContainers[TraceMarker::MAX_NUM_ARGS];
|
|
for (int i = 0; i < std::min(num_args, TraceMarker::MAX_NUM_ARGS); ++i) {
|
|
auto& arg = *args[i];
|
|
arg.mPassed = true;
|
|
arg.mName = ProfilerString8View::WrapNullTerminatedString(arg_names[i]);
|
|
switch (arg_types[i]) {
|
|
case TRACE_VALUE_TYPE_UINT:
|
|
MOZ_ASSERT(arg_values[i] <= std::numeric_limits<int64_t>::max());
|
|
arg.mValue = AsVariant(static_cast<int64_t>(
|
|
reinterpret_cast<const TraceValueUnion*>(&arg_values[i])->as_uint));
|
|
break;
|
|
case TRACE_VALUE_TYPE_INT:
|
|
arg.mValue = AsVariant(static_cast<int64_t>(
|
|
reinterpret_cast<const TraceValueUnion*>(&arg_values[i])->as_int));
|
|
break;
|
|
case TRACE_VALUE_TYPE_BOOL:
|
|
arg.mValue = AsVariant(
|
|
reinterpret_cast<const TraceValueUnion*>(&arg_values[i])->as_bool);
|
|
break;
|
|
case TRACE_VALUE_TYPE_DOUBLE:
|
|
arg.mValue =
|
|
AsVariant(reinterpret_cast<const TraceValueUnion*>(&arg_values[i])
|
|
->as_double);
|
|
break;
|
|
case TRACE_VALUE_TYPE_POINTER:
|
|
strValueContainers[i] = nsPrintfCString(
|
|
"%p", reinterpret_cast<const TraceValueUnion*>(&arg_values[i])
|
|
->as_pointer);
|
|
arg.mValue = AsVariant(ProfilerString8View(strValueContainers[i]));
|
|
break;
|
|
case TRACE_VALUE_TYPE_STRING:
|
|
arg.mValue = AsVariant(ProfilerString8View::WrapNullTerminatedString(
|
|
reinterpret_cast<const TraceValueUnion*>(&arg_values[i])
|
|
->as_string));
|
|
break;
|
|
case TRACE_VALUE_TYPE_COPY_STRING:
|
|
strValueContainers[i] =
|
|
reinterpret_cast<const TraceValueUnion*>(&arg_values[i])->as_string;
|
|
arg.mValue = AsVariant(ProfilerString8View(strValueContainers[i]));
|
|
break;
|
|
default:
|
|
MOZ_ASSERT_UNREACHABLE("Unexpected trace value type");
|
|
strValueContainers[i] =
|
|
nsPrintfCString("Unexpected type: %u", arg_types[i]);
|
|
arg.mValue = AsVariant(ProfilerString8View(strValueContainers[i]));
|
|
break;
|
|
}
|
|
}
|
|
|
|
profiler_add_marker(
|
|
ProfilerString8View::WrapNullTerminatedString(name), ToCategory(category),
|
|
{timing.extract(),
|
|
capture_stack
|
|
? MarkerStack::Capture(StackCaptureOptions::Full)
|
|
: (provided_stack
|
|
? MarkerStack::UseBacktrace(
|
|
*(static_cast<mozilla::ProfileChunkedBuffer*>(
|
|
provided_stack)))
|
|
: MarkerStack::Capture(StackCaptureOptions::NoStack))},
|
|
TraceMarker{}, tuple);
|
|
#endif // MOZ_GECKO_PROFILER
|
|
}
|
|
|
|
void uprofiler_simple_event_marker_capture_stack(
|
|
const char* name, const char category, char phase, int num_args,
|
|
const char** arg_names, const unsigned char* arg_types,
|
|
const unsigned long long* arg_values) {
|
|
uprofiler_simple_event_marker_internal(
|
|
name, category, phase, num_args, arg_names, arg_types, arg_values, true);
|
|
}
|
|
|
|
void uprofiler_simple_event_marker_with_stack(
|
|
const char* name, const char category, char phase, int num_args,
|
|
const char** arg_names, const unsigned char* arg_types,
|
|
const unsigned long long* arg_values, void* provided_stack) {
|
|
MOZ_ASSERT(provided_stack != nullptr);
|
|
uprofiler_simple_event_marker_internal(name, category, phase, num_args,
|
|
arg_names, arg_types, arg_values,
|
|
false, provided_stack);
|
|
}
|
|
|
|
void uprofiler_simple_event_marker(const char* name, const char category,
|
|
char phase, int num_args,
|
|
const char** arg_names,
|
|
const unsigned char* arg_types,
|
|
const unsigned long long* arg_values) {
|
|
uprofiler_simple_event_marker_internal(name, category, phase, num_args,
|
|
arg_names, arg_types, arg_values);
|
|
}
|
|
|
|
bool uprofiler_backtrace_into_buffer(NativeStack* aNativeStack, void* aBuffer) {
|
|
#if defined(MOZ_GECKO_PROFILER)
|
|
return profiler_backtrace_into_buffer(
|
|
*(static_cast<mozilla::ProfileChunkedBuffer*>(aBuffer)), *aNativeStack);
|
|
#else
|
|
return false;
|
|
#endif
|
|
}
|
|
|
|
void uprofiler_native_backtrace(const void* top, NativeStack* nativeStack) {
|
|
#if defined(MOZ_GECKO_PROFILER)
|
|
DoNativeBacktraceDirect(top, *nativeStack, nullptr);
|
|
#endif
|
|
}
|
|
|
|
bool uprofiler_is_active() { return profiler_is_active(); }
|
|
|
|
bool uprofiler_feature_active(int32_t aFeature) {
|
|
return profiler_feature_active(aFeature);
|
|
}
|
|
|
|
bool uprofiler_get(struct UprofilerFuncPtrs* aFuncPtrs) {
|
|
if (!aFuncPtrs) {
|
|
return false;
|
|
}
|
|
|
|
aFuncPtrs->register_thread = uprofiler_register_thread;
|
|
aFuncPtrs->unregister_thread = uprofiler_unregister_thread;
|
|
aFuncPtrs->simple_event_marker = uprofiler_simple_event_marker;
|
|
aFuncPtrs->simple_event_marker_capture_stack =
|
|
uprofiler_simple_event_marker_capture_stack;
|
|
aFuncPtrs->simple_event_marker_with_stack =
|
|
uprofiler_simple_event_marker_with_stack;
|
|
aFuncPtrs->backtrace_into_buffer = uprofiler_backtrace_into_buffer;
|
|
aFuncPtrs->native_backtrace = uprofiler_native_backtrace;
|
|
aFuncPtrs->is_active = uprofiler_is_active;
|
|
aFuncPtrs->feature_active = uprofiler_feature_active;
|
|
|
|
return true;
|
|
}
|