const char* ThreadStackHelper::AppendJSEntry(const volatile StackEntry* aEntry, intptr_t& aAvailableBufferSize, const char* aPrevLabel) { // May be called from another thread or inside a signal handler. // We assume querying the script is safe but we must not manupulate it. // Also we must not allocate any memory from heap. MOZ_ASSERT(aEntry->isJs()); MOZ_ASSERT(aEntry->script()); const char* label; if (IsChromeJSScript(aEntry->script())) { const char* filename = JS_GetScriptFilename(aEntry->script()); const unsigned lineno = JS_PCToLineNumber(aEntry->script(), aEntry->pc()); MOZ_ASSERT(filename); char buffer[128]; // Enough to fit longest js file name from the tree // Some script names are in the form "foo -> bar -> baz". // Here we find the origin of these redirected scripts. const char* basename = GetPathAfterComponent(filename, " -> "); if (basename) { filename = basename; } basename = GetFullPathForScheme(filename, "chrome://"); if (!basename) { basename = GetFullPathForScheme(filename, "resource://"); } if (!basename) { // If the (add-on) script is located under the {profile}/extensions // directory, extract the path after the /extensions/ part. basename = GetPathAfterComponent(filename, "/extensions/"); } if (!basename) { // Only keep the file base name for paths outside the above formats. basename = strrchr(filename, '/'); basename = basename ? basename + 1 : filename; // Look for Windows path separator as well. filename = strrchr(basename, '\\'); if (filename) { basename = filename + 1; } } size_t len = snprintf_literal(buffer, "%s:%u", basename, lineno); if (len < sizeof(buffer)) { if (mStackToFill->IsSameAsEntry(aPrevLabel, buffer)) { return aPrevLabel; } // Keep track of the required buffer size aAvailableBufferSize -= (len + 1); if (aAvailableBufferSize >= 0) { // Buffer is big enough. return mStackToFill->InfallibleAppendViaBuffer(buffer, len); } // Buffer is not big enough; fall through to using static label below. } // snprintf failed or buffer is not big enough. label = "(chrome script)"; } else { label = "(content script)"; } if (mStackToFill->IsSameAsEntry(aPrevLabel, label)) { return aPrevLabel; } mStackToFill->infallibleAppend(label); return label; }
void ThreadStackHelper::CollectPseudoEntry(const js::ProfileEntry& aEntry) { // For non-js frames we just include the raw label. if (!aEntry.isJs()) { const char* entryLabel = aEntry.label(); // entryLabel is a statically allocated string, so we want to store a // reference to it without performing any allocations. This is important, as // we aren't allowed to allocate within this function. // // The variant for this kind of label in our HangStack object is a // `nsCString`, which normally contains heap allocated string data. However, // `nsCString` has an optimization for literal strings which causes the // backing data to not be copied when being copied between nsCString // objects. // // We take advantage of that optimization by creating a nsCString object // which has the LITERAL flag set. Without this optimization, this code // would be incorrect. nsCString label; label.AssignLiteral(entryLabel, strlen(entryLabel)); // Let's make sure we don't deadlock here, by asserting that `label`'s // backing data matches. MOZ_RELEASE_ASSERT(label.BeginReading() == entryLabel, "String copy performed during ThreadStackHelper::CollectPseudoEntry"); TryAppendFrame(label); return; } if (!aEntry.script()) { TryAppendFrame(HangEntrySuppressed()); return; } if (!IsChromeJSScript(aEntry.script())) { TryAppendFrame(HangEntryContent()); return; } // Rather than using the profiler's dynamic string, we compute our own string. // This is because we want to do some size-saving strategies, and throw out // information which won't help us as much. // XXX: We currently don't collect the function name which hung. const char* filename = JS_GetScriptFilename(aEntry.script()); unsigned lineno = JS_PCToLineNumber(aEntry.script(), aEntry.pc()); // Some script names are in the form "foo -> bar -> baz". // Here we find the origin of these redirected scripts. const char* basename = GetPathAfterComponent(filename, " -> "); if (basename) { filename = basename; } // Strip chrome:// or resource:// off of the filename if present. basename = GetFullPathForScheme(filename, "chrome://"); if (!basename) { basename = GetFullPathForScheme(filename, "resource://"); } if (!basename) { // If we're in an add-on script, under the {profile}/extensions // directory, extract the path after the /extensions/ part. basename = GetPathAfterComponent(filename, "/extensions/"); } if (!basename) { // Only keep the file base name for paths outside the above formats. basename = strrchr(filename, '/'); basename = basename ? basename + 1 : filename; // Look for Windows path separator as well. filename = strrchr(basename, '\\'); if (filename) { basename = filename + 1; } } char buffer[128]; // Enough to fit longest js file name from the tree size_t len = SprintfLiteral(buffer, "%s:%u", basename, lineno); if (len < sizeof(buffer)) { mDesiredBufferSize += len + 1; if (mStackToFill->stack().Capacity() > mStackToFill->stack().Length() && (mStackToFill->strbuffer().Capacity() - mStackToFill->strbuffer().Length()) > len + 1) { // NOTE: We only increment this if we're going to successfully append. mDesiredStackSize += 1; uint32_t start = mStackToFill->strbuffer().Length(); mStackToFill->strbuffer().AppendElements(buffer, len); mStackToFill->strbuffer().AppendElement('\0'); mStackToFill->stack().AppendElement(HangEntryBufOffset(start)); return; } } TryAppendFrame(HangEntryChromeScript()); }