ProfiledThreadData.cpp (6077B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "ProfiledThreadData.h" 8 9 #include "ProfileBuffer.h" 10 11 #include "mozilla/BaseProfiler.h" 12 #include "mozilla/BaseProfileJSONWriter.h" 13 14 #if defined(GP_OS_darwin) 15 # include <pthread.h> 16 #endif 17 18 namespace mozilla { 19 namespace baseprofiler { 20 21 ProfiledThreadData::ProfiledThreadData(ThreadInfo* aThreadInfo) 22 : mThreadInfo(aThreadInfo) {} 23 24 ProfiledThreadData::~ProfiledThreadData() {} 25 26 void ProfiledThreadData::StreamJSON(const ProfileBuffer& aBuffer, 27 SpliceableJSONWriter& aWriter, 28 const std::string& aProcessName, 29 const std::string& aETLDplus1, 30 const TimeStamp& aProcessStartTime, 31 double aSinceTime) { 32 UniqueStacks uniqueStacks; 33 34 aWriter.SetUniqueStrings(uniqueStacks.UniqueStrings()); 35 36 aWriter.Start(); 37 { 38 StreamSamplesAndMarkers(mThreadInfo->Name(), mThreadInfo->ThreadId(), 39 aBuffer, aWriter, aProcessName, aETLDplus1, 40 aProcessStartTime, mThreadInfo->RegisterTime(), 41 mUnregisterTime, aSinceTime, uniqueStacks); 42 43 aWriter.StartObjectProperty("stackTable"); 44 { 45 { 46 JSONSchemaWriter schema(aWriter); 47 schema.WriteField("prefix"); 48 schema.WriteField("frame"); 49 } 50 51 aWriter.StartArrayProperty("data"); 52 { 53 uniqueStacks.SpliceStackTableElements(aWriter); 54 } 55 aWriter.EndArray(); 56 } 57 aWriter.EndObject(); 58 59 aWriter.StartObjectProperty("frameTable"); 60 { 61 { 62 JSONSchemaWriter schema(aWriter); 63 schema.WriteField("location"); 64 schema.WriteField("relevantForJS"); 65 schema.WriteField("innerWindowID"); 66 schema.WriteField("implementation"); 67 schema.WriteField("line"); 68 schema.WriteField("column"); 69 schema.WriteField("category"); 70 schema.WriteField("subcategory"); 71 } 72 73 aWriter.StartArrayProperty("data"); 74 { 75 uniqueStacks.SpliceFrameTableElements(aWriter); 76 } 77 aWriter.EndArray(); 78 } 79 aWriter.EndObject(); 80 81 aWriter.StartArrayProperty("stringTable"); 82 { 83 std::move(uniqueStacks.UniqueStrings()) 84 .SpliceStringTableElements(aWriter); 85 } 86 aWriter.EndArray(); 87 } 88 aWriter.End(); 89 90 aWriter.ResetUniqueStrings(); 91 } 92 93 BaseProfilerThreadId StreamSamplesAndMarkers( 94 const char* aName, BaseProfilerThreadId aThreadId, 95 const ProfileBuffer& aBuffer, SpliceableJSONWriter& aWriter, 96 const std::string& aProcessName, const std::string& aETLDplus1, 97 const TimeStamp& aProcessStartTime, const TimeStamp& aRegisterTime, 98 const TimeStamp& aUnregisterTime, double aSinceTime, 99 UniqueStacks& aUniqueStacks) { 100 BaseProfilerThreadId processedThreadId; 101 102 aWriter.StringProperty( 103 "processType", 104 "(unknown)" /* XRE_GeckoProcessTypeToString(XRE_GetProcessType()) */); 105 106 { 107 std::string name = aName; 108 // We currently need to distinguish threads output by Base Profiler from 109 // those in Gecko Profiler, as the frontend could get confused and lose 110 // tracks with the same name. 111 // TODO: As part of the profilers de-duplication, thread data from both 112 // profilers should end up in the same track, at which point this won't be 113 // necessary anymore. See meta bug 1557566. 114 name += " (pre-xul)"; 115 aWriter.StringProperty("name", name); 116 } 117 118 // Use given process name (if any). 119 if (!aProcessName.empty()) { 120 aWriter.StringProperty("processName", aProcessName); 121 } 122 if (!aETLDplus1.empty()) { 123 aWriter.StringProperty("eTLD+1", aETLDplus1); 124 } 125 126 if (aRegisterTime) { 127 aWriter.DoubleProperty( 128 "registerTime", (aRegisterTime - aProcessStartTime).ToMilliseconds()); 129 } else { 130 aWriter.NullProperty("registerTime"); 131 } 132 133 if (aUnregisterTime) { 134 aWriter.DoubleProperty( 135 "unregisterTime", 136 (aUnregisterTime - aProcessStartTime).ToMilliseconds()); 137 } else { 138 aWriter.NullProperty("unregisterTime"); 139 } 140 141 aWriter.StartObjectProperty("samples"); 142 { 143 { 144 JSONSchemaWriter schema(aWriter); 145 schema.WriteField("stack"); 146 schema.WriteField("time"); 147 schema.WriteField("eventDelay"); 148 } 149 150 aWriter.StartArrayProperty("data"); 151 { 152 processedThreadId = aBuffer.StreamSamplesToJSON( 153 aWriter, aThreadId, aSinceTime, aUniqueStacks); 154 } 155 aWriter.EndArray(); 156 } 157 aWriter.EndObject(); 158 159 aWriter.StartObjectProperty("markers"); 160 { 161 { 162 JSONSchemaWriter schema(aWriter); 163 schema.WriteField("name"); 164 schema.WriteField("startTime"); 165 schema.WriteField("endTime"); 166 schema.WriteField("phase"); 167 schema.WriteField("category"); 168 schema.WriteField("data"); 169 } 170 171 aWriter.StartArrayProperty("data"); 172 { 173 aBuffer.StreamMarkersToJSON(aWriter, aThreadId, aProcessStartTime, 174 aSinceTime, aUniqueStacks); 175 } 176 aWriter.EndArray(); 177 } 178 aWriter.EndObject(); 179 180 // Tech note: If `ToNumber()` returns a uint64_t, the conversion to int64_t is 181 // "implementation-defined" before C++20. This is acceptable here, because 182 // this is a one-way conversion to a unique identifier that's used to visually 183 // separate data by thread on the front-end. 184 aWriter.IntProperty( 185 "pid", static_cast<int64_t>(profiler_current_process_id().ToNumber())); 186 aWriter.IntProperty("tid", 187 static_cast<int64_t>(aThreadId.IsSpecified() 188 ? aThreadId.ToNumber() 189 : processedThreadId.ToNumber())); 190 191 return processedThreadId; 192 } 193 194 } // namespace baseprofiler 195 } // namespace mozilla