/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 "ProfiledThreadData.h" #include "BaseProfiler.h" #include "ProfileBuffer.h" #include "mozilla/BaseProfileJSONWriter.h" #if defined(GP_OS_darwin) # include #endif namespace mozilla { namespace baseprofiler { ProfiledThreadData::ProfiledThreadData(ThreadInfo* aThreadInfo) : mThreadInfo(aThreadInfo) {} ProfiledThreadData::~ProfiledThreadData() {} void ProfiledThreadData::StreamJSON(const ProfileBuffer& aBuffer, SpliceableJSONWriter& aWriter, const std::string& aProcessName, const std::string& aETLDplus1, const TimeStamp& aProcessStartTime, double aSinceTime) { UniqueStacks uniqueStacks; aWriter.SetUniqueStrings(uniqueStacks.UniqueStrings()); aWriter.Start(); { StreamSamplesAndMarkers(mThreadInfo->Name(), mThreadInfo->ThreadId(), aBuffer, aWriter, aProcessName, aETLDplus1, aProcessStartTime, mThreadInfo->RegisterTime(), mUnregisterTime, aSinceTime, uniqueStacks); aWriter.StartObjectProperty("stackTable"); { { JSONSchemaWriter schema(aWriter); schema.WriteField("prefix"); schema.WriteField("frame"); } aWriter.StartArrayProperty("data"); { uniqueStacks.SpliceStackTableElements(aWriter); } aWriter.EndArray(); } aWriter.EndObject(); aWriter.StartObjectProperty("frameTable"); { { JSONSchemaWriter schema(aWriter); schema.WriteField("location"); schema.WriteField("relevantForJS"); schema.WriteField("innerWindowID"); schema.WriteField("implementation"); schema.WriteField("line"); schema.WriteField("column"); schema.WriteField("category"); schema.WriteField("subcategory"); } aWriter.StartArrayProperty("data"); { uniqueStacks.SpliceFrameTableElements(aWriter); } aWriter.EndArray(); } aWriter.EndObject(); aWriter.StartArrayProperty("stringTable"); { std::move(uniqueStacks.UniqueStrings()) .SpliceStringTableElements(aWriter); } aWriter.EndArray(); } aWriter.End(); aWriter.ResetUniqueStrings(); } BaseProfilerThreadId StreamSamplesAndMarkers( const char* aName, BaseProfilerThreadId aThreadId, const ProfileBuffer& aBuffer, SpliceableJSONWriter& aWriter, const std::string& aProcessName, const std::string& aETLDplus1, const TimeStamp& aProcessStartTime, const TimeStamp& aRegisterTime, const TimeStamp& aUnregisterTime, double aSinceTime, UniqueStacks& aUniqueStacks) { BaseProfilerThreadId processedThreadId; aWriter.StringProperty( "processType", "(unknown)" /* XRE_GeckoProcessTypeToString(XRE_GetProcessType()) */); { std::string name = aName; // We currently need to distinguish threads output by Base Profiler from // those in Gecko Profiler, as the frontend could get confused and lose // tracks with the same name. // TODO: As part of the profilers de-duplication, thread data from both // profilers should end up in the same track, at which point this won't be // necessary anymore. See meta bug 1557566. name += " (pre-xul)"; aWriter.StringProperty("name", name); } // Use given process name (if any). if (!aProcessName.empty()) { aWriter.StringProperty("processName", aProcessName); } if (!aETLDplus1.empty()) { aWriter.StringProperty("eTLD+1", aETLDplus1); } if (aRegisterTime) { aWriter.DoubleProperty( "registerTime", (aRegisterTime - aProcessStartTime).ToMilliseconds()); } else { aWriter.NullProperty("registerTime"); } if (aUnregisterTime) { aWriter.DoubleProperty( "unregisterTime", (aUnregisterTime - aProcessStartTime).ToMilliseconds()); } else { aWriter.NullProperty("unregisterTime"); } aWriter.StartObjectProperty("samples"); { { JSONSchemaWriter schema(aWriter); schema.WriteField("stack"); schema.WriteField("time"); schema.WriteField("eventDelay"); } aWriter.StartArrayProperty("data"); { processedThreadId = aBuffer.StreamSamplesToJSON( aWriter, aThreadId, aSinceTime, aUniqueStacks); } aWriter.EndArray(); } aWriter.EndObject(); aWriter.StartObjectProperty("markers"); { { JSONSchemaWriter schema(aWriter); schema.WriteField("name"); schema.WriteField("startTime"); schema.WriteField("endTime"); schema.WriteField("phase"); schema.WriteField("category"); schema.WriteField("data"); } aWriter.StartArrayProperty("data"); { aBuffer.StreamMarkersToJSON(aWriter, aThreadId, aProcessStartTime, aSinceTime, aUniqueStacks); } aWriter.EndArray(); } aWriter.EndObject(); // Tech note: If `ToNumber()` returns a uint64_t, the conversion to int64_t is // "implementation-defined" before C++20. This is acceptable here, because // this is a one-way conversion to a unique identifier that's used to visually // separate data by thread on the front-end. aWriter.IntProperty( "pid", static_cast(profiler_current_process_id().ToNumber())); aWriter.IntProperty("tid", static_cast(aThreadId.IsSpecified() ? aThreadId.ToNumber() : processedThreadId.ToNumber())); return processedThreadId; } } // namespace baseprofiler } // namespace mozilla