1/*
2 * Copyright (C) 2012 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "CodeProfile.h"
28
29#include "CodeBlock.h"
30#include "CodeProfiling.h"
31#include "LinkBuffer.h"
32#include "ProfileTreeNode.h"
33#include <wtf/StackTrace.h>
34#include <wtf/text/WTFString.h>
35
36namespace JSC {
37
38// Map from CodeType enum to a corresponding name.
39static const char* const s_codeTypeNames[CodeProfile::NumberOfCodeTypes] = {
40 "[[EngineCode]]",
41 "[[GlobalThunk]]",
42 "[[RegExpCode]]",
43 "[[DFGJIT]]",
44 "[[BaselineOnly]]",
45 "[[BaselineProfile]]",
46 "[[BaselineOSR]]",
47 "[[EngineFrame]]"
48};
49
50// Helper function, truncate traces to prune the output & make very verbose mode a little more readable.
51static bool truncateTrace(const char* symbolName)
52{
53 return !strcmp(symbolName, "JSC::BytecodeGenerator::generate()")
54 || !strcmp(symbolName, "JSC::Parser<JSC::Lexer<unsigned char>>::parseInner()")
55 || !strcmp(symbolName, "WTF::fastMalloc(unsigned long)")
56 || !strcmp(symbolName, "WTF::calculateUTCOffset()")
57 || !strcmp(symbolName, "JSC::DFG::ByteCodeParser::parseCodeBlock()");
58
59}
60
61// Each trace consists of a sequence of zero or more 'EngineFrame' entries,
62// followed by a sample in JIT code, or one or more 'EngineFrame' entries,
63// followed by a 'EngineCode' terminator.
64void CodeProfile::sample(void* pc, void** framePointer)
65{
66 // Disallow traces containing only a 'EngineCode' terminator, without any 'EngineFrame' frames.
67 if (!framePointer)
68 return;
69
70 while (framePointer) {
71 CodeType type;
72
73#if ENABLE(JIT)
74 // Determine if this sample fell in JIT code, and if so, from which JIT & why.
75 void* ownerUID = CodeProfiling::getOwnerUIDForPC(pc);
76
77 if (!ownerUID)
78 type = EngineFrame;
79 else if (ownerUID == GLOBAL_THUNK_ID)
80 type = GlobalThunk;
81 else if (ownerUID == REGEXP_CODE_ID)
82 type = RegExpCode;
83 else {
84 CodeBlock* codeBlock = static_cast<CodeBlock*>(ownerUID);
85 if (codeBlock->jitType() == JITType::DFGJIT)
86 type = DFGJIT;
87 else if (!canCompile(codeBlock->capabilityLevelState()))
88 type = BaselineOnly;
89 else if (codeBlock->replacement())
90 type = BaselineOSR;
91 else
92 type = BaselineProfile;
93 }
94#else
95 type = EngineFrame;
96#endif
97
98 // A sample in JIT code terminates the trace.
99 m_samples.append(CodeRecord(pc, type));
100 if (type != EngineFrame)
101 return;
102
103#if OS(DARWIN) && CPU(X86_64)
104 // Walk up the stack.
105 pc = framePointer[1];
106 framePointer = reinterpret_cast<void**>(*framePointer);
107#elif OS(LINUX) && CPU(X86)
108 // Don't unwind the stack as some dependent third party libraries
109 // may be compiled with -fomit-frame-pointer.
110 framePointer = 0;
111#else
112 // This platform is not yet supported!
113 RELEASE_ASSERT_NOT_REACHED();
114#endif
115 }
116
117 // If we get here, we walked the entire stack without finding any frames of JIT code.
118 m_samples.append(CodeRecord(0, EngineCode));
119}
120
121void CodeProfile::report()
122{
123 dataLogF("<CodeProfiling %s:%d>\n", m_file.data(), m_lineNumber);
124
125 // How many frames of C-code to print - 0, if not verbose, 1 if verbose, up to 1024 if very verbose.
126 unsigned recursionLimit = CodeProfiling::beVeryVerbose() ? 1024 : CodeProfiling::beVerbose();
127
128 ProfileTreeNode profile;
129
130 // Walk through the sample buffer.
131 size_t trace = 0;
132 while (trace < m_samples.size()) {
133
134 // All traces are zero or more 'EngineFrame's, followed by a non-'EngineFrame'.
135 // Scan to find the last sample in the trace.
136 size_t lastInTrace = trace;
137 while (m_samples[lastInTrace].type == EngineFrame)
138 ++lastInTrace;
139
140 // We use the last sample type to look up a name (used as a bucket in the profiler).
141 ProfileTreeNode* callbacks = profile.sampleChild(s_codeTypeNames[m_samples[lastInTrace].type]);
142
143 // If there are any samples in C-code, add up to recursionLimit of them into the profile tree.
144 size_t lastEngineFrame = lastInTrace;
145 for (unsigned count = 0; lastEngineFrame > trace && count < recursionLimit; ++count) {
146 --lastEngineFrame;
147 ASSERT(m_samples[lastEngineFrame].type == EngineFrame);
148 const char* name = "<unknown>";
149 auto demangled = StackTrace::demangle(m_samples[lastEngineFrame].pc);
150 if (demangled)
151 name = demangled->demangledName() ? demangled->demangledName() : demangled->mangledName();
152 callbacks = callbacks->sampleChild(name);
153 if (truncateTrace(name))
154 break;
155 }
156
157 // Move on to the next trace.
158 trace = lastInTrace + 1;
159 ASSERT(trace <= m_samples.size());
160 }
161
162 // Output the profile tree.
163 dataLogF("Total samples: %lld\n", static_cast<long long>(profile.childCount()));
164 profile.dump();
165
166 for (size_t i = 0 ; i < m_children.size(); ++i)
167 m_children[i]->report();
168
169 dataLogF("</CodeProfiling %s:%d>\n", m_file.data(), m_lineNumber);
170}
171
172}
173