Wed, 31 Dec 2014 06:09:35 +0100
Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
michael@0 | 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | #include <ostream> |
michael@0 | 7 | #include <sstream> |
michael@0 | 8 | #include "platform.h" |
michael@0 | 9 | #include "nsThreadUtils.h" |
michael@0 | 10 | #include "nsXULAppAPI.h" |
michael@0 | 11 | #include "jsapi.h" |
michael@0 | 12 | |
michael@0 | 13 | // JSON |
michael@0 | 14 | #include "JSStreamWriter.h" |
michael@0 | 15 | |
michael@0 | 16 | // Self |
michael@0 | 17 | #include "ProfileEntry.h" |
michael@0 | 18 | |
michael@0 | 19 | #if _MSC_VER |
michael@0 | 20 | #define snprintf _snprintf |
michael@0 | 21 | #endif |
michael@0 | 22 | |
michael@0 | 23 | //////////////////////////////////////////////////////////////////////// |
michael@0 | 24 | // BEGIN ProfileEntry |
michael@0 | 25 | |
michael@0 | 26 | ProfileEntry::ProfileEntry() |
michael@0 | 27 | : mTagData(nullptr) |
michael@0 | 28 | , mTagName(0) |
michael@0 | 29 | { } |
michael@0 | 30 | |
michael@0 | 31 | // aTagData must not need release (i.e. be a string from the text segment) |
michael@0 | 32 | ProfileEntry::ProfileEntry(char aTagName, const char *aTagData) |
michael@0 | 33 | : mTagData(aTagData) |
michael@0 | 34 | , mTagName(aTagName) |
michael@0 | 35 | { } |
michael@0 | 36 | |
michael@0 | 37 | ProfileEntry::ProfileEntry(char aTagName, ProfilerMarker *aTagMarker) |
michael@0 | 38 | : mTagMarker(aTagMarker) |
michael@0 | 39 | , mTagName(aTagName) |
michael@0 | 40 | { } |
michael@0 | 41 | |
michael@0 | 42 | ProfileEntry::ProfileEntry(char aTagName, void *aTagPtr) |
michael@0 | 43 | : mTagPtr(aTagPtr) |
michael@0 | 44 | , mTagName(aTagName) |
michael@0 | 45 | { } |
michael@0 | 46 | |
michael@0 | 47 | ProfileEntry::ProfileEntry(char aTagName, float aTagFloat) |
michael@0 | 48 | : mTagFloat(aTagFloat) |
michael@0 | 49 | , mTagName(aTagName) |
michael@0 | 50 | { } |
michael@0 | 51 | |
michael@0 | 52 | ProfileEntry::ProfileEntry(char aTagName, uintptr_t aTagOffset) |
michael@0 | 53 | : mTagOffset(aTagOffset) |
michael@0 | 54 | , mTagName(aTagName) |
michael@0 | 55 | { } |
michael@0 | 56 | |
michael@0 | 57 | ProfileEntry::ProfileEntry(char aTagName, Address aTagAddress) |
michael@0 | 58 | : mTagAddress(aTagAddress) |
michael@0 | 59 | , mTagName(aTagName) |
michael@0 | 60 | { } |
michael@0 | 61 | |
michael@0 | 62 | ProfileEntry::ProfileEntry(char aTagName, int aTagLine) |
michael@0 | 63 | : mTagLine(aTagLine) |
michael@0 | 64 | , mTagName(aTagName) |
michael@0 | 65 | { } |
michael@0 | 66 | |
michael@0 | 67 | ProfileEntry::ProfileEntry(char aTagName, char aTagChar) |
michael@0 | 68 | : mTagChar(aTagChar) |
michael@0 | 69 | , mTagName(aTagName) |
michael@0 | 70 | { } |
michael@0 | 71 | |
michael@0 | 72 | bool ProfileEntry::is_ent_hint(char hintChar) { |
michael@0 | 73 | return mTagName == 'h' && mTagChar == hintChar; |
michael@0 | 74 | } |
michael@0 | 75 | |
michael@0 | 76 | bool ProfileEntry::is_ent_hint() { |
michael@0 | 77 | return mTagName == 'h'; |
michael@0 | 78 | } |
michael@0 | 79 | |
michael@0 | 80 | bool ProfileEntry::is_ent(char tagChar) { |
michael@0 | 81 | return mTagName == tagChar; |
michael@0 | 82 | } |
michael@0 | 83 | |
michael@0 | 84 | void* ProfileEntry::get_tagPtr() { |
michael@0 | 85 | // No consistency checking. Oh well. |
michael@0 | 86 | return mTagPtr; |
michael@0 | 87 | } |
michael@0 | 88 | |
michael@0 | 89 | void ProfileEntry::log() |
michael@0 | 90 | { |
michael@0 | 91 | // There is no compiler enforced mapping between tag chars |
michael@0 | 92 | // and union variant fields, so the following was derived |
michael@0 | 93 | // by looking through all the use points of TableTicker.cpp. |
michael@0 | 94 | // mTagMarker (ProfilerMarker*) m |
michael@0 | 95 | // mTagData (const char*) c,s |
michael@0 | 96 | // mTagPtr (void*) d,l,L,B (immediate backtrace), S(start-of-stack) |
michael@0 | 97 | // mTagLine (int) n,f |
michael@0 | 98 | // mTagChar (char) h |
michael@0 | 99 | // mTagFloat (double) r,t,p |
michael@0 | 100 | switch (mTagName) { |
michael@0 | 101 | case 'm': |
michael@0 | 102 | LOGF("%c \"%s\"", mTagName, mTagMarker->GetMarkerName()); break; |
michael@0 | 103 | case 'c': case 's': |
michael@0 | 104 | LOGF("%c \"%s\"", mTagName, mTagData); break; |
michael@0 | 105 | case 'd': case 'l': case 'L': case 'B': case 'S': |
michael@0 | 106 | LOGF("%c %p", mTagName, mTagPtr); break; |
michael@0 | 107 | case 'n': case 'f': |
michael@0 | 108 | LOGF("%c %d", mTagName, mTagLine); break; |
michael@0 | 109 | case 'h': |
michael@0 | 110 | LOGF("%c \'%c\'", mTagName, mTagChar); break; |
michael@0 | 111 | case 'r': case 't': case 'p': |
michael@0 | 112 | LOGF("%c %f", mTagName, mTagFloat); break; |
michael@0 | 113 | default: |
michael@0 | 114 | LOGF("'%c' unknown_tag", mTagName); break; |
michael@0 | 115 | } |
michael@0 | 116 | } |
michael@0 | 117 | |
michael@0 | 118 | std::ostream& operator<<(std::ostream& stream, const ProfileEntry& entry) |
michael@0 | 119 | { |
michael@0 | 120 | if (entry.mTagName == 'r' || entry.mTagName == 't') { |
michael@0 | 121 | stream << entry.mTagName << "-" << std::fixed << entry.mTagFloat << "\n"; |
michael@0 | 122 | } else if (entry.mTagName == 'l' || entry.mTagName == 'L') { |
michael@0 | 123 | // Bug 739800 - Force l-tag addresses to have a "0x" prefix on all platforms |
michael@0 | 124 | // Additionally, stringstream seemed to be ignoring formatter flags. |
michael@0 | 125 | char tagBuff[1024]; |
michael@0 | 126 | unsigned long long pc = (unsigned long long)(uintptr_t)entry.mTagPtr; |
michael@0 | 127 | snprintf(tagBuff, 1024, "%c-%#llx\n", entry.mTagName, pc); |
michael@0 | 128 | stream << tagBuff; |
michael@0 | 129 | } else if (entry.mTagName == 'd') { |
michael@0 | 130 | // TODO implement 'd' tag for text profile |
michael@0 | 131 | } else { |
michael@0 | 132 | stream << entry.mTagName << "-" << entry.mTagData << "\n"; |
michael@0 | 133 | } |
michael@0 | 134 | return stream; |
michael@0 | 135 | } |
michael@0 | 136 | |
michael@0 | 137 | // END ProfileEntry |
michael@0 | 138 | //////////////////////////////////////////////////////////////////////// |
michael@0 | 139 | |
michael@0 | 140 | |
michael@0 | 141 | //////////////////////////////////////////////////////////////////////// |
michael@0 | 142 | // BEGIN ThreadProfile |
michael@0 | 143 | |
michael@0 | 144 | #define DYNAMIC_MAX_STRING 512 |
michael@0 | 145 | |
michael@0 | 146 | ThreadProfile::ThreadProfile(const char* aName, int aEntrySize, |
michael@0 | 147 | PseudoStack *aStack, Thread::tid_t aThreadId, |
michael@0 | 148 | PlatformData* aPlatform, |
michael@0 | 149 | bool aIsMainThread, void *aStackTop) |
michael@0 | 150 | : mWritePos(0) |
michael@0 | 151 | , mLastFlushPos(0) |
michael@0 | 152 | , mReadPos(0) |
michael@0 | 153 | , mEntrySize(aEntrySize) |
michael@0 | 154 | , mPseudoStack(aStack) |
michael@0 | 155 | , mMutex("ThreadProfile::mMutex") |
michael@0 | 156 | , mName(strdup(aName)) |
michael@0 | 157 | , mThreadId(aThreadId) |
michael@0 | 158 | , mIsMainThread(aIsMainThread) |
michael@0 | 159 | , mPlatformData(aPlatform) |
michael@0 | 160 | , mGeneration(0) |
michael@0 | 161 | , mPendingGenerationFlush(0) |
michael@0 | 162 | , mStackTop(aStackTop) |
michael@0 | 163 | { |
michael@0 | 164 | mEntries = new ProfileEntry[mEntrySize]; |
michael@0 | 165 | } |
michael@0 | 166 | |
michael@0 | 167 | ThreadProfile::~ThreadProfile() |
michael@0 | 168 | { |
michael@0 | 169 | free(mName); |
michael@0 | 170 | delete[] mEntries; |
michael@0 | 171 | } |
michael@0 | 172 | |
michael@0 | 173 | void ThreadProfile::addTag(ProfileEntry aTag) |
michael@0 | 174 | { |
michael@0 | 175 | // Called from signal, call only reentrant functions |
michael@0 | 176 | mEntries[mWritePos] = aTag; |
michael@0 | 177 | mWritePos = mWritePos + 1; |
michael@0 | 178 | if (mWritePos >= mEntrySize) { |
michael@0 | 179 | mPendingGenerationFlush++; |
michael@0 | 180 | mWritePos = mWritePos % mEntrySize; |
michael@0 | 181 | } |
michael@0 | 182 | if (mWritePos == mReadPos) { |
michael@0 | 183 | // Keep one slot open |
michael@0 | 184 | mEntries[mReadPos] = ProfileEntry(); |
michael@0 | 185 | mReadPos = (mReadPos + 1) % mEntrySize; |
michael@0 | 186 | } |
michael@0 | 187 | // we also need to move the flush pos to ensure we |
michael@0 | 188 | // do not pass it |
michael@0 | 189 | if (mWritePos == mLastFlushPos) { |
michael@0 | 190 | mLastFlushPos = (mLastFlushPos + 1) % mEntrySize; |
michael@0 | 191 | } |
michael@0 | 192 | } |
michael@0 | 193 | |
michael@0 | 194 | // flush the new entries |
michael@0 | 195 | void ThreadProfile::flush() |
michael@0 | 196 | { |
michael@0 | 197 | mLastFlushPos = mWritePos; |
michael@0 | 198 | mGeneration += mPendingGenerationFlush; |
michael@0 | 199 | mPendingGenerationFlush = 0; |
michael@0 | 200 | } |
michael@0 | 201 | |
michael@0 | 202 | // discards all of the entries since the last flush() |
michael@0 | 203 | // NOTE: that if mWritePos happens to wrap around past |
michael@0 | 204 | // mLastFlushPos we actually only discard mWritePos - mLastFlushPos entries |
michael@0 | 205 | // |
michael@0 | 206 | // r = mReadPos |
michael@0 | 207 | // w = mWritePos |
michael@0 | 208 | // f = mLastFlushPos |
michael@0 | 209 | // |
michael@0 | 210 | // r f w |
michael@0 | 211 | // |-----------------------------| |
michael@0 | 212 | // | abcdefghijklmnopq | -> 'abcdefghijklmnopq' |
michael@0 | 213 | // |-----------------------------| |
michael@0 | 214 | // |
michael@0 | 215 | // |
michael@0 | 216 | // mWritePos and mReadPos have passed mLastFlushPos |
michael@0 | 217 | // f |
michael@0 | 218 | // w r |
michael@0 | 219 | // |-----------------------------| |
michael@0 | 220 | // |ABCDEFGHIJKLMNOPQRSqrstuvwxyz| |
michael@0 | 221 | // |-----------------------------| |
michael@0 | 222 | // w |
michael@0 | 223 | // r |
michael@0 | 224 | // |-----------------------------| |
michael@0 | 225 | // |ABCDEFGHIJKLMNOPQRSqrstuvwxyz| -> '' |
michael@0 | 226 | // |-----------------------------| |
michael@0 | 227 | // |
michael@0 | 228 | // |
michael@0 | 229 | // mWritePos will end up the same as mReadPos |
michael@0 | 230 | // r |
michael@0 | 231 | // w f |
michael@0 | 232 | // |-----------------------------| |
michael@0 | 233 | // |ABCDEFGHIJKLMklmnopqrstuvwxyz| |
michael@0 | 234 | // |-----------------------------| |
michael@0 | 235 | // r |
michael@0 | 236 | // w |
michael@0 | 237 | // |-----------------------------| |
michael@0 | 238 | // |ABCDEFGHIJKLMklmnopqrstuvwxyz| -> '' |
michael@0 | 239 | // |-----------------------------| |
michael@0 | 240 | // |
michael@0 | 241 | // |
michael@0 | 242 | // mWritePos has moved past mReadPos |
michael@0 | 243 | // w r f |
michael@0 | 244 | // |-----------------------------| |
michael@0 | 245 | // |ABCDEFdefghijklmnopqrstuvwxyz| |
michael@0 | 246 | // |-----------------------------| |
michael@0 | 247 | // r w |
michael@0 | 248 | // |-----------------------------| |
michael@0 | 249 | // |ABCDEFdefghijklmnopqrstuvwxyz| -> 'defghijkl' |
michael@0 | 250 | // |-----------------------------| |
michael@0 | 251 | |
michael@0 | 252 | void ThreadProfile::erase() |
michael@0 | 253 | { |
michael@0 | 254 | mWritePos = mLastFlushPos; |
michael@0 | 255 | mPendingGenerationFlush = 0; |
michael@0 | 256 | } |
michael@0 | 257 | |
michael@0 | 258 | char* ThreadProfile::processDynamicTag(int readPos, |
michael@0 | 259 | int* tagsConsumed, char* tagBuff) |
michael@0 | 260 | { |
michael@0 | 261 | int readAheadPos = (readPos + 1) % mEntrySize; |
michael@0 | 262 | int tagBuffPos = 0; |
michael@0 | 263 | |
michael@0 | 264 | // Read the string stored in mTagData until the null character is seen |
michael@0 | 265 | bool seenNullByte = false; |
michael@0 | 266 | while (readAheadPos != mLastFlushPos && !seenNullByte) { |
michael@0 | 267 | (*tagsConsumed)++; |
michael@0 | 268 | ProfileEntry readAheadEntry = mEntries[readAheadPos]; |
michael@0 | 269 | for (size_t pos = 0; pos < sizeof(void*); pos++) { |
michael@0 | 270 | tagBuff[tagBuffPos] = readAheadEntry.mTagChars[pos]; |
michael@0 | 271 | if (tagBuff[tagBuffPos] == '\0' || tagBuffPos == DYNAMIC_MAX_STRING-2) { |
michael@0 | 272 | seenNullByte = true; |
michael@0 | 273 | break; |
michael@0 | 274 | } |
michael@0 | 275 | tagBuffPos++; |
michael@0 | 276 | } |
michael@0 | 277 | if (!seenNullByte) |
michael@0 | 278 | readAheadPos = (readAheadPos + 1) % mEntrySize; |
michael@0 | 279 | } |
michael@0 | 280 | return tagBuff; |
michael@0 | 281 | } |
michael@0 | 282 | |
michael@0 | 283 | void ThreadProfile::IterateTags(IterateTagsCallback aCallback) |
michael@0 | 284 | { |
michael@0 | 285 | MOZ_ASSERT(aCallback); |
michael@0 | 286 | |
michael@0 | 287 | int readPos = mReadPos; |
michael@0 | 288 | while (readPos != mLastFlushPos) { |
michael@0 | 289 | // Number of tag consumed |
michael@0 | 290 | int incBy = 1; |
michael@0 | 291 | const ProfileEntry& entry = mEntries[readPos]; |
michael@0 | 292 | |
michael@0 | 293 | // Read ahead to the next tag, if it's a 'd' tag process it now |
michael@0 | 294 | const char* tagStringData = entry.mTagData; |
michael@0 | 295 | int readAheadPos = (readPos + 1) % mEntrySize; |
michael@0 | 296 | char tagBuff[DYNAMIC_MAX_STRING]; |
michael@0 | 297 | // Make sure the string is always null terminated if it fills up DYNAMIC_MAX_STRING-2 |
michael@0 | 298 | tagBuff[DYNAMIC_MAX_STRING-1] = '\0'; |
michael@0 | 299 | |
michael@0 | 300 | if (readAheadPos != mLastFlushPos && mEntries[readAheadPos].mTagName == 'd') { |
michael@0 | 301 | tagStringData = processDynamicTag(readPos, &incBy, tagBuff); |
michael@0 | 302 | } |
michael@0 | 303 | |
michael@0 | 304 | aCallback(entry, tagStringData); |
michael@0 | 305 | |
michael@0 | 306 | readPos = (readPos + incBy) % mEntrySize; |
michael@0 | 307 | } |
michael@0 | 308 | } |
michael@0 | 309 | |
michael@0 | 310 | void ThreadProfile::ToStreamAsJSON(std::ostream& stream) |
michael@0 | 311 | { |
michael@0 | 312 | JSStreamWriter b(stream); |
michael@0 | 313 | StreamJSObject(b); |
michael@0 | 314 | } |
michael@0 | 315 | |
michael@0 | 316 | void ThreadProfile::StreamJSObject(JSStreamWriter& b) |
michael@0 | 317 | { |
michael@0 | 318 | b.BeginObject(); |
michael@0 | 319 | // Thread meta data |
michael@0 | 320 | if (XRE_GetProcessType() == GeckoProcessType_Plugin) { |
michael@0 | 321 | // TODO Add the proper plugin name |
michael@0 | 322 | b.NameValue("name", "Plugin"); |
michael@0 | 323 | } else { |
michael@0 | 324 | b.NameValue("name", mName); |
michael@0 | 325 | } |
michael@0 | 326 | b.NameValue("tid", static_cast<int>(mThreadId)); |
michael@0 | 327 | |
michael@0 | 328 | b.Name("samples"); |
michael@0 | 329 | b.BeginArray(); |
michael@0 | 330 | |
michael@0 | 331 | bool sample = false; |
michael@0 | 332 | int readPos = mReadPos; |
michael@0 | 333 | while (readPos != mLastFlushPos) { |
michael@0 | 334 | // Number of tag consumed |
michael@0 | 335 | ProfileEntry entry = mEntries[readPos]; |
michael@0 | 336 | |
michael@0 | 337 | switch (entry.mTagName) { |
michael@0 | 338 | case 'r': |
michael@0 | 339 | { |
michael@0 | 340 | if (sample) { |
michael@0 | 341 | b.NameValue("responsiveness", entry.mTagFloat); |
michael@0 | 342 | } |
michael@0 | 343 | } |
michael@0 | 344 | break; |
michael@0 | 345 | case 'p': |
michael@0 | 346 | { |
michael@0 | 347 | if (sample) { |
michael@0 | 348 | b.NameValue("power", entry.mTagFloat); |
michael@0 | 349 | } |
michael@0 | 350 | } |
michael@0 | 351 | break; |
michael@0 | 352 | case 'f': |
michael@0 | 353 | { |
michael@0 | 354 | if (sample) { |
michael@0 | 355 | b.NameValue("frameNumber", entry.mTagLine); |
michael@0 | 356 | } |
michael@0 | 357 | } |
michael@0 | 358 | break; |
michael@0 | 359 | case 't': |
michael@0 | 360 | { |
michael@0 | 361 | if (sample) { |
michael@0 | 362 | b.NameValue("time", entry.mTagFloat); |
michael@0 | 363 | } |
michael@0 | 364 | } |
michael@0 | 365 | break; |
michael@0 | 366 | case 's': |
michael@0 | 367 | { |
michael@0 | 368 | // end the previous sample if there was one |
michael@0 | 369 | if (sample) { |
michael@0 | 370 | b.EndObject(); |
michael@0 | 371 | } |
michael@0 | 372 | // begin the next sample |
michael@0 | 373 | b.BeginObject(); |
michael@0 | 374 | |
michael@0 | 375 | sample = true; |
michael@0 | 376 | |
michael@0 | 377 | // Seek forward through the entire sample, looking for frames |
michael@0 | 378 | // this is an easier approach to reason about than adding more |
michael@0 | 379 | // control variables and cases to the loop that goes through the buffer once |
michael@0 | 380 | b.Name("frames"); |
michael@0 | 381 | b.BeginArray(); |
michael@0 | 382 | |
michael@0 | 383 | b.BeginObject(); |
michael@0 | 384 | b.NameValue("location", "(root)"); |
michael@0 | 385 | b.EndObject(); |
michael@0 | 386 | |
michael@0 | 387 | int framePos = (readPos + 1) % mEntrySize; |
michael@0 | 388 | ProfileEntry frame = mEntries[framePos]; |
michael@0 | 389 | while (framePos != mLastFlushPos && frame.mTagName != 's') { |
michael@0 | 390 | int incBy = 1; |
michael@0 | 391 | frame = mEntries[framePos]; |
michael@0 | 392 | // Read ahead to the next tag, if it's a 'd' tag process it now |
michael@0 | 393 | const char* tagStringData = frame.mTagData; |
michael@0 | 394 | int readAheadPos = (framePos + 1) % mEntrySize; |
michael@0 | 395 | char tagBuff[DYNAMIC_MAX_STRING]; |
michael@0 | 396 | // Make sure the string is always null terminated if it fills up |
michael@0 | 397 | // DYNAMIC_MAX_STRING-2 |
michael@0 | 398 | tagBuff[DYNAMIC_MAX_STRING-1] = '\0'; |
michael@0 | 399 | |
michael@0 | 400 | if (readAheadPos != mLastFlushPos && mEntries[readAheadPos].mTagName == 'd') { |
michael@0 | 401 | tagStringData = processDynamicTag(framePos, &incBy, tagBuff); |
michael@0 | 402 | } |
michael@0 | 403 | |
michael@0 | 404 | // Write one frame. It can have either |
michael@0 | 405 | // 1. only location - 'l' containing a memory address |
michael@0 | 406 | // 2. location and line number - 'c' followed by 'd's and an optional 'n' |
michael@0 | 407 | if (frame.mTagName == 'l') { |
michael@0 | 408 | b.BeginObject(); |
michael@0 | 409 | // Bug 753041 |
michael@0 | 410 | // We need a double cast here to tell GCC that we don't want to sign |
michael@0 | 411 | // extend 32-bit addresses starting with 0xFXXXXXX. |
michael@0 | 412 | unsigned long long pc = (unsigned long long)(uintptr_t)frame.mTagPtr; |
michael@0 | 413 | snprintf(tagBuff, DYNAMIC_MAX_STRING, "%#llx", pc); |
michael@0 | 414 | b.NameValue("location", tagBuff); |
michael@0 | 415 | b.EndObject(); |
michael@0 | 416 | } else if (frame.mTagName == 'c') { |
michael@0 | 417 | b.BeginObject(); |
michael@0 | 418 | b.NameValue("location", tagStringData); |
michael@0 | 419 | readAheadPos = (framePos + incBy) % mEntrySize; |
michael@0 | 420 | if (readAheadPos != mLastFlushPos && |
michael@0 | 421 | mEntries[readAheadPos].mTagName == 'n') { |
michael@0 | 422 | b.NameValue("line", mEntries[readAheadPos].mTagLine); |
michael@0 | 423 | incBy++; |
michael@0 | 424 | } |
michael@0 | 425 | b.EndObject(); |
michael@0 | 426 | } |
michael@0 | 427 | framePos = (framePos + incBy) % mEntrySize; |
michael@0 | 428 | } |
michael@0 | 429 | b.EndArray(); |
michael@0 | 430 | } |
michael@0 | 431 | break; |
michael@0 | 432 | } |
michael@0 | 433 | readPos = (readPos + 1) % mEntrySize; |
michael@0 | 434 | } |
michael@0 | 435 | if (sample) { |
michael@0 | 436 | b.EndObject(); |
michael@0 | 437 | } |
michael@0 | 438 | b.EndArray(); |
michael@0 | 439 | |
michael@0 | 440 | b.Name("markers"); |
michael@0 | 441 | b.BeginArray(); |
michael@0 | 442 | readPos = mReadPos; |
michael@0 | 443 | while (readPos != mLastFlushPos) { |
michael@0 | 444 | ProfileEntry entry = mEntries[readPos]; |
michael@0 | 445 | if (entry.mTagName == 'm') { |
michael@0 | 446 | entry.getMarker()->StreamJSObject(b); |
michael@0 | 447 | } |
michael@0 | 448 | readPos = (readPos + 1) % mEntrySize; |
michael@0 | 449 | } |
michael@0 | 450 | b.EndArray(); |
michael@0 | 451 | b.EndObject(); |
michael@0 | 452 | } |
michael@0 | 453 | |
michael@0 | 454 | JSObject* ThreadProfile::ToJSObject(JSContext *aCx) |
michael@0 | 455 | { |
michael@0 | 456 | JS::RootedValue val(aCx); |
michael@0 | 457 | std::stringstream ss; |
michael@0 | 458 | { |
michael@0 | 459 | // Define a scope to prevent a moving GC during ~JSStreamWriter from |
michael@0 | 460 | // trashing the return value. |
michael@0 | 461 | JSStreamWriter b(ss); |
michael@0 | 462 | StreamJSObject(b); |
michael@0 | 463 | NS_ConvertUTF8toUTF16 js_string(nsDependentCString(ss.str().c_str())); |
michael@0 | 464 | JS_ParseJSON(aCx, static_cast<const jschar*>(js_string.get()), js_string.Length(), &val); |
michael@0 | 465 | } |
michael@0 | 466 | return &val.toObject(); |
michael@0 | 467 | } |
michael@0 | 468 | |
michael@0 | 469 | PseudoStack* ThreadProfile::GetPseudoStack() |
michael@0 | 470 | { |
michael@0 | 471 | return mPseudoStack; |
michael@0 | 472 | } |
michael@0 | 473 | |
michael@0 | 474 | void ThreadProfile::BeginUnwind() |
michael@0 | 475 | { |
michael@0 | 476 | mMutex.Lock(); |
michael@0 | 477 | } |
michael@0 | 478 | |
michael@0 | 479 | void ThreadProfile::EndUnwind() |
michael@0 | 480 | { |
michael@0 | 481 | mMutex.Unlock(); |
michael@0 | 482 | } |
michael@0 | 483 | |
michael@0 | 484 | mozilla::Mutex* ThreadProfile::GetMutex() |
michael@0 | 485 | { |
michael@0 | 486 | return &mMutex; |
michael@0 | 487 | } |
michael@0 | 488 | |
michael@0 | 489 | void ThreadProfile::DuplicateLastSample() { |
michael@0 | 490 | // Scan the whole buffer (even unflushed parts) |
michael@0 | 491 | // Adding mEntrySize makes the result of the modulus positive |
michael@0 | 492 | // We search backwards from mWritePos-1 to mReadPos |
michael@0 | 493 | for (int readPos = (mWritePos + mEntrySize - 1) % mEntrySize; |
michael@0 | 494 | readPos != (mReadPos + mEntrySize - 1) % mEntrySize; |
michael@0 | 495 | readPos = (readPos + mEntrySize - 1) % mEntrySize) { |
michael@0 | 496 | if (mEntries[readPos].mTagName == 's') { |
michael@0 | 497 | // Found the start of the last entry at position readPos |
michael@0 | 498 | int copyEndIdx = mWritePos; |
michael@0 | 499 | // Go through the whole entry and duplicate it |
michael@0 | 500 | for (;readPos != copyEndIdx; readPos = (readPos + 1) % mEntrySize) { |
michael@0 | 501 | switch (mEntries[readPos].mTagName) { |
michael@0 | 502 | // Copy with new time |
michael@0 | 503 | case 't': |
michael@0 | 504 | addTag(ProfileEntry('t', static_cast<float>((mozilla::TimeStamp::Now() - sStartTime).ToMilliseconds()))); |
michael@0 | 505 | break; |
michael@0 | 506 | // Don't copy markers |
michael@0 | 507 | case 'm': |
michael@0 | 508 | break; |
michael@0 | 509 | // Copy anything else we don't know about |
michael@0 | 510 | // L, B, S, c, s, d, l, f, h, r, t, p |
michael@0 | 511 | default: |
michael@0 | 512 | addTag(mEntries[readPos]); |
michael@0 | 513 | break; |
michael@0 | 514 | } |
michael@0 | 515 | } |
michael@0 | 516 | break; |
michael@0 | 517 | } |
michael@0 | 518 | } |
michael@0 | 519 | } |
michael@0 | 520 | |
michael@0 | 521 | std::ostream& operator<<(std::ostream& stream, const ThreadProfile& profile) |
michael@0 | 522 | { |
michael@0 | 523 | int readPos = profile.mReadPos; |
michael@0 | 524 | while (readPos != profile.mLastFlushPos) { |
michael@0 | 525 | stream << profile.mEntries[readPos]; |
michael@0 | 526 | readPos = (readPos + 1) % profile.mEntrySize; |
michael@0 | 527 | } |
michael@0 | 528 | return stream; |
michael@0 | 529 | } |
michael@0 | 530 | |
michael@0 | 531 | // END ThreadProfile |
michael@0 | 532 | //////////////////////////////////////////////////////////////////////// |