ProfileBuffer.cpp (7765B)
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 "ProfileBuffer.h" 8 9 #include "mozilla/BaseProfiler.h" 10 11 namespace mozilla { 12 namespace baseprofiler { 13 14 ProfileBuffer::ProfileBuffer(ProfileChunkedBuffer& aBuffer) 15 : mEntries(aBuffer) { 16 // Assume the given buffer is in-session. 17 MOZ_ASSERT(mEntries.IsInSession()); 18 } 19 20 /* static */ 21 ProfileBufferBlockIndex ProfileBuffer::AddEntry( 22 ProfileChunkedBuffer& aProfileChunkedBuffer, 23 const ProfileBufferEntry& aEntry) { 24 switch (aEntry.GetKind()) { 25 #define SWITCH_KIND(KIND, TYPE, SIZE) \ 26 case ProfileBufferEntry::Kind::KIND: { \ 27 return aProfileChunkedBuffer.PutFrom(&aEntry, 1 + (SIZE)); \ 28 break; \ 29 } 30 31 FOR_EACH_PROFILE_BUFFER_ENTRY_KIND(SWITCH_KIND) 32 33 #undef SWITCH_KIND 34 default: 35 MOZ_ASSERT(false, "Unhandled baseprofiler::ProfilerBuffer entry KIND"); 36 return ProfileBufferBlockIndex{}; 37 } 38 } 39 40 // Called from signal, call only reentrant functions 41 uint64_t ProfileBuffer::AddEntry(const ProfileBufferEntry& aEntry) { 42 return AddEntry(mEntries, aEntry).ConvertToProfileBufferIndex(); 43 } 44 45 /* static */ 46 ProfileBufferBlockIndex ProfileBuffer::AddThreadIdEntry( 47 ProfileChunkedBuffer& aProfileChunkedBuffer, 48 BaseProfilerThreadId aThreadId) { 49 return AddEntry(aProfileChunkedBuffer, 50 ProfileBufferEntry::ThreadId(aThreadId)); 51 } 52 53 uint64_t ProfileBuffer::AddThreadIdEntry(BaseProfilerThreadId aThreadId) { 54 return AddThreadIdEntry(mEntries, aThreadId).ConvertToProfileBufferIndex(); 55 } 56 57 void ProfileBuffer::CollectCodeLocation( 58 const char* aLabel, const char* aStr, uint32_t aFrameFlags, 59 uint64_t aInnerWindowID, uint32_t aSourceId, 60 const Maybe<uint32_t>& aLineNumber, const Maybe<uint32_t>& aColumnNumber, 61 const Maybe<ProfilingCategoryPair>& aCategoryPair) { 62 AddEntry(ProfileBufferEntry::Label(aLabel)); 63 AddEntry(ProfileBufferEntry::FrameFlags(uint64_t(aFrameFlags))); 64 65 if (aStr) { 66 // Store the string using one or more DynamicStringFragment entries. 67 size_t strLen = strlen(aStr) + 1; // +1 for the null terminator 68 // If larger than the prescribed limit, we will cut the string and end it 69 // with an ellipsis. 70 const bool tooBig = strLen > kMaxFrameKeyLength; 71 if (tooBig) { 72 strLen = kMaxFrameKeyLength; 73 } 74 char chars[ProfileBufferEntry::kNumChars]; 75 for (size_t j = 0;; j += ProfileBufferEntry::kNumChars) { 76 // Store up to kNumChars characters in the entry. 77 size_t len = ProfileBufferEntry::kNumChars; 78 const bool last = j + len >= strLen; 79 if (last) { 80 // Only the last entry may be smaller than kNumChars. 81 len = strLen - j; 82 if (tooBig) { 83 // That last entry is part of a too-big string, replace the end 84 // characters with an ellipsis "...". 85 len = std::max(len, size_t(4)); 86 chars[len - 4] = '.'; 87 chars[len - 3] = '.'; 88 chars[len - 2] = '.'; 89 chars[len - 1] = '\0'; 90 // Make sure the memcpy will not overwrite our ellipsis! 91 len -= 4; 92 } 93 } 94 memcpy(chars, &aStr[j], len); 95 AddEntry(ProfileBufferEntry::DynamicStringFragment(chars)); 96 if (last) { 97 break; 98 } 99 } 100 } 101 102 if (aInnerWindowID) { 103 AddEntry(ProfileBufferEntry::InnerWindowID(aInnerWindowID)); 104 } 105 106 if (aSourceId) { 107 AddEntry(ProfileBufferEntry::SourceId(aSourceId)); 108 } 109 110 if (aLineNumber) { 111 AddEntry(ProfileBufferEntry::LineNumber(*aLineNumber)); 112 } 113 114 if (aColumnNumber) { 115 AddEntry(ProfileBufferEntry::ColumnNumber(*aColumnNumber)); 116 } 117 118 if (aCategoryPair.isSome()) { 119 AddEntry(ProfileBufferEntry::CategoryPair(int(*aCategoryPair))); 120 } 121 } 122 123 size_t ProfileBuffer::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { 124 // Measurement of the following members may be added later if DMD finds it 125 // is worthwhile: 126 // - memory pointed to by the elements within mEntries 127 return mEntries.SizeOfExcludingThis(aMallocSizeOf); 128 } 129 130 size_t ProfileBuffer::SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const { 131 return aMallocSizeOf(this) + SizeOfExcludingThis(aMallocSizeOf); 132 } 133 134 void ProfileBuffer::CollectOverheadStats(TimeDuration aSamplingTime, 135 TimeDuration aLocking, 136 TimeDuration aCleaning, 137 TimeDuration aCounters, 138 TimeDuration aThreads) { 139 double timeUs = aSamplingTime.ToMilliseconds() * 1000.0; 140 if (mFirstSamplingTimeUs == 0.0) { 141 mFirstSamplingTimeUs = timeUs; 142 } else { 143 // Note that we'll have 1 fewer interval than other numbers (because 144 // we need both ends of an interval to know its duration). The final 145 // difference should be insignificant over the expected many thousands 146 // of iterations. 147 mIntervalsUs.Count(timeUs - mLastSamplingTimeUs); 148 } 149 mLastSamplingTimeUs = timeUs; 150 // Time to take the lock before sampling. 151 double lockingUs = aLocking.ToMilliseconds() * 1000.0; 152 // Time to discard expired data. 153 double cleaningUs = aCleaning.ToMilliseconds() * 1000.0; 154 // Time to gather all counters. 155 double countersUs = aCounters.ToMilliseconds() * 1000.0; 156 // Time to sample all threads. 157 double threadsUs = aThreads.ToMilliseconds() * 1000.0; 158 159 // Add to our gathered stats. 160 mOverheadsUs.Count(lockingUs + cleaningUs + countersUs + threadsUs); 161 mLockingsUs.Count(lockingUs); 162 mCleaningsUs.Count(cleaningUs); 163 mCountersUs.Count(countersUs); 164 mThreadsUs.Count(threadsUs); 165 166 // Record details in buffer, if requested. 167 static const bool sRecordSamplingOverhead = []() { 168 const char* recordOverheads = getenv("MOZ_PROFILER_RECORD_OVERHEADS"); 169 return recordOverheads && recordOverheads[0] != '\0'; 170 }(); 171 if (sRecordSamplingOverhead) { 172 AddEntry(ProfileBufferEntry::ProfilerOverheadTime(timeUs)); 173 AddEntry(ProfileBufferEntry::ProfilerOverheadDuration(lockingUs)); 174 AddEntry(ProfileBufferEntry::ProfilerOverheadDuration(cleaningUs)); 175 AddEntry(ProfileBufferEntry::ProfilerOverheadDuration(countersUs)); 176 AddEntry(ProfileBufferEntry::ProfilerOverheadDuration(threadsUs)); 177 } 178 } 179 180 ProfilerBufferInfo ProfileBuffer::GetProfilerBufferInfo() const { 181 return {BufferRangeStart(), 182 BufferRangeEnd(), 183 static_cast<uint32_t>(*mEntries.BufferLength() / 184 8), // 8 bytes per entry. 185 mIntervalsUs, 186 mOverheadsUs, 187 mLockingsUs, 188 mCleaningsUs, 189 mCountersUs, 190 mThreadsUs}; 191 } 192 193 /* ProfileBufferCollector */ 194 195 void ProfileBufferCollector::CollectNativeLeafAddr(void* aAddr) { 196 mBuf.AddEntry(ProfileBufferEntry::NativeLeafAddr(aAddr)); 197 } 198 199 void ProfileBufferCollector::CollectProfilingStackFrame( 200 const ProfilingStackFrame& aFrame) { 201 // WARNING: this function runs within the profiler's "critical section". 202 203 MOZ_ASSERT(aFrame.isLabelFrame() || 204 (aFrame.isJsFrame() && !aFrame.isOSRFrame())); 205 206 const char* label = aFrame.label(); 207 const char* dynamicString = aFrame.dynamicString(); 208 Maybe<uint32_t> line; 209 Maybe<uint32_t> column; 210 211 MOZ_ASSERT(aFrame.isLabelFrame()); 212 213 mBuf.CollectCodeLocation(label, dynamicString, aFrame.flags(), 214 aFrame.realmID(), 0, line, column, 215 Some(aFrame.categoryPair())); 216 } 217 218 } // namespace baseprofiler 219 } // namespace mozilla