Skip to content

Commit bccc78c

Browse files
committed
[JSC] Add IRDump option with JITDump
https://bugs.webkit.org/show_bug.cgi?id=309344 rdar://171891036 Reviewed by Keith Miller. This patch adds useIRDump and irDumpDirectory options. They are effective only when useJITDump is enabled. When it is enabled, we generate IRDump, which is just a very simple sequence of DFG IR as a file. And we also generate JITDump's JITCodeDebugInfo with this file. This debug info can associate a JIT code range to a file line & column. By using them, we associate JIT code with this on-the-fly generated IRDump files so profiler can show samples in IRDump as well. * Source/JavaScriptCore/assembler/LinkBuffer.cpp: (JSC::LinkBuffer::logJITCodeForJITDump): * Source/JavaScriptCore/assembler/LinkBuffer.h: (JSC::IRDumpDebugInfo::IRDumpDebugInfo): (JSC::LinkBuffer::setIRDumpDebugInfo): * Source/JavaScriptCore/assembler/PerfLog.cpp: (JSC::PerfLog::PerfLog): (JSC::PerfLog::log): * Source/JavaScriptCore/assembler/PerfLog.h: * Source/JavaScriptCore/dfg/DFGGraph.cpp: (JSC::DFG::Graph::collectIRDumpDebugInfo): * Source/JavaScriptCore/dfg/DFGGraph.h: * Source/JavaScriptCore/dfg/DFGJITCompiler.cpp: (JSC::DFG::JITCompiler::collectIRDumpDebugInfo): * Source/JavaScriptCore/dfg/DFGJITCompiler.h: (JSC::DFG::JITCompiler::setForNode): * Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp: (JSC::DFG::SpeculativeJIT::compile): (JSC::DFG::SpeculativeJIT::compileFunction): * Source/JavaScriptCore/ftl/FTLCompile.cpp: (JSC::FTL::compile): * Source/JavaScriptCore/ftl/FTLState.cpp: (JSC::FTL::State::State): * Source/JavaScriptCore/runtime/Options.cpp: (JSC::Options::notifyOptionsChanged): * Source/JavaScriptCore/runtime/OptionsList.h: Canonical link: https://commits.webkit.org/308822@main
1 parent 29f5a44 commit bccc78c

13 files changed

Lines changed: 233 additions & 5 deletions

File tree

‎Source/JavaScriptCore/assembler/LinkBuffer.cpp‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ namespace JSC {
4747
size_t LinkBuffer::s_profileCummulativeLinkedSizes[LinkBuffer::numberOfProfiles];
4848
size_t LinkBuffer::s_profileCummulativeLinkedCounts[LinkBuffer::numberOfProfiles];
4949

50+
WTF_MAKE_TZONE_ALLOCATED_IMPL(IRDumpDebugInfo);
5051
WTF_MAKE_TZONE_ALLOCATED_IMPL(LinkBuffer);
5152

5253
static const char* profileName(LinkBuffer::Profile profile)
@@ -127,7 +128,7 @@ void LinkBuffer::logJITCodeForJITDump(CodeRef<LinkBufferPtrTag>& codeRef, ASCIIL
127128
GdbJIT::log(finalName, codeRef);
128129

129130
if (Options::useJITDump()) [[unlikely]]
130-
PerfLog::log(finalName, codeRef);
131+
PerfLog::log(finalName, codeRef, WTF::move(m_irDumpDebugInfo));
131132
}
132133

133134
LinkBuffer::CodeRef<LinkBufferPtrTag> LinkBuffer::finalizeCodeWithDisassemblyImpl(bool dumpDisassembly, ASCIILiteral simpleName, const char* format, ...)

‎Source/JavaScriptCore/assembler/LinkBuffer.h‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,39 @@
4040
#include <JavaScriptCore/MacroAssemblerCodeRef.h>
4141
#include <wtf/DataLog.h>
4242
#include <wtf/TZoneMalloc.h>
43+
#include <wtf/text/CString.h>
4344

4445
WTF_ALLOW_UNSAFE_BUFFER_USAGE_BEGIN
4546

4647
namespace JSC {
4748

49+
class IRDumpDebugInfo {
50+
WTF_MAKE_TZONE_ALLOCATED(IRDumpDebugInfo);
51+
WTF_MAKE_NONCOPYABLE(IRDumpDebugInfo);
52+
public:
53+
// This is per-line either BasicBlock header annotation or actual IR node. Like,
54+
// BB#0
55+
// GetLocal
56+
struct IRLine {
57+
ASCIILiteral opName;
58+
uint32_t blockIndex { 0 };
59+
};
60+
61+
struct CodeEntry {
62+
uint32_t codeOffset;
63+
uint32_t irLineIndex;
64+
};
65+
66+
IRDumpDebugInfo(CString&& name)
67+
: functionName(WTF::move(name))
68+
{
69+
}
70+
71+
CString functionName;
72+
Vector<IRLine> irLines;
73+
Vector<CodeEntry> codeEntries;
74+
};
75+
4876
// LinkBuffer:
4977
//
5078
// This class assists in linking code generated by the macro assembler, once code generation
@@ -328,6 +356,8 @@ ALLOW_NONLITERAL_FORMAT_END
328356

329357
void setIsThunk() { m_isThunk = true; }
330358

359+
void setIRDumpDebugInfo(std::unique_ptr<IRDumpDebugInfo>&& info) { m_irDumpDebugInfo = WTF::move(info); }
360+
331361
private:
332362
JS_EXPORT_PRIVATE CodeRef<LinkBufferPtrTag> finalizeCodeWithoutDisassemblyImpl(ASCIILiteral);
333363
JS_EXPORT_PRIVATE CodeRef<LinkBufferPtrTag> finalizeCodeWithDisassemblyImpl(bool dumpDisassembly, ASCIILiteral, const char* format, ...) WTF_ATTRIBUTE_PRINTF(4, 5);
@@ -414,6 +444,7 @@ ALLOW_NONLITERAL_FORMAT_END
414444
CodePtr<LinkBufferPtrTag> m_code;
415445
Vector<Ref<SharedTask<void(LinkBuffer&)>>> m_linkTasks;
416446
Vector<Ref<SharedTask<void(LinkBuffer&)>>> m_lateLinkTasks;
447+
std::unique_ptr<IRDumpDebugInfo> m_irDumpDebugInfo;
417448

418449
static size_t s_profileCummulativeLinkedSizes[numberOfProfiles];
419450
static size_t s_profileCummulativeLinkedCounts[numberOfProfiles];

‎Source/JavaScriptCore/assembler/PerfLog.cpp‎

Lines changed: 80 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,22 @@ struct CodeLoadRecord {
144144
uint64_t codeIndex { 0 };
145145
};
146146

147+
struct DebugInfoRecord {
148+
RecordHeader header {
149+
RecordType::JITCodeDebugInfo,
150+
0,
151+
0,
152+
};
153+
uint64_t codeAddress { 0 };
154+
uint64_t nrEntry { 0 };
155+
};
156+
157+
struct DebugEntry {
158+
uint64_t codeAddress { 0 };
159+
uint32_t line { 0 };
160+
uint32_t discrim { 0 };
161+
};
162+
147163
} // namespace JITDump
148164

149165
WTF_MAKE_TZONE_ALLOCATED_IMPL(PerfLog);
@@ -164,6 +180,9 @@ PerfLog::PerfLog()
164180
m_file = FileSystem::createDumpFile(makeString("jit-"_s, ProfilerSupport::getCurrentThreadID(), "-"_s, WTF::getCurrentProcessID()), ".dump"_s, String::fromUTF8(Options::jitDumpDirectory()));
165181
RELEASE_ASSERT(m_file);
166182

183+
if (Options::useIRDump())
184+
m_irDumpDirectory = Options::irDumpDirectory();
185+
167186
#if OS(LINUX)
168187
// Linux perf command records this mmap operation in perf.data as a metadata to the JIT perf annotations.
169188
// We do not use this mmap-ed memory region actually.
@@ -192,11 +211,11 @@ void PerfLog::flush(const AbstractLocker&)
192211
m_file.flush();
193212
}
194213

195-
void PerfLog::log(const CString& name, MacroAssemblerCodeRef<LinkBufferPtrTag> code)
214+
void PerfLog::log(const CString& name, MacroAssemblerCodeRef<LinkBufferPtrTag> code, std::unique_ptr<IRDumpDebugInfo>&& debugInfo)
196215
{
197216
auto timestamp = ProfilerSupport::generateTimestamp();
198217
auto tid = ProfilerSupport::getCurrentThreadID();
199-
ProfilerSupport::singleton().queue().dispatch([name = name, code, tid, timestamp] {
218+
ProfilerSupport::singleton().queue().dispatch([name = name, code, tid, timestamp, debugInfo = WTF::move(debugInfo)] {
200219
PerfLog& logger = singleton();
201220
size_t size = code.size();
202221
auto* executableAddress = code.code().untaggedPtr<const uint8_t*>();
@@ -205,8 +224,67 @@ void PerfLog::log(const CString& name, MacroAssemblerCodeRef<LinkBufferPtrTag> c
205224
return;
206225
}
207226

227+
CString irFilePath;
228+
Vector<std::pair<uint32_t, uint32_t>> lineEntries;
229+
if (debugInfo) {
230+
auto baseName = makeString("irdump-"_s, String::fromUTF8(debugInfo->functionName.span()), "-"_s, WTF::getCurrentProcessID(), "-"_s, timestamp);
231+
232+
String filePath;
233+
FileSystem::FileHandle handle;
234+
const CString& irDumpDir = logger.m_irDumpDirectory;
235+
if (irDumpDir.isNull()) {
236+
auto result = FileSystem::openTemporaryFile(baseName, ".txt"_s);
237+
filePath = result.first;
238+
handle = WTF::move(result.second);
239+
} else {
240+
filePath = makeString(String::fromUTF8(irDumpDir.span()), FileSystem::pathSeparator, baseName, ".txt"_s);
241+
handle = FileSystem::openFile(filePath, FileSystem::FileOpenMode::Truncate);
242+
}
243+
244+
if (handle) {
245+
// Write sequential IR dump file from irLines.
246+
for (auto& irLine : debugInfo->irLines) {
247+
CString line;
248+
if (irLine.opName)
249+
line = toCString(" ", irLine.opName, "\n");
250+
else
251+
line = toCString("BB#", irLine.blockIndex, "\n");
252+
handle.write(WTF::asByteSpan(line.span()));
253+
}
254+
handle.flush();
255+
irFilePath = FileSystem::fileSystemRepresentation(filePath);
256+
257+
// Convert code entries to (codeOffset, 1-based lineNumber) pairs.
258+
for (auto& codeEntry : debugInfo->codeEntries)
259+
lineEntries.append({ codeEntry.codeOffset, codeEntry.irLineIndex + 1 });
260+
}
261+
}
262+
208263
Locker locker { logger.m_lock };
209264

265+
if (!irFilePath.isNull() && !lineEntries.isEmpty()) {
266+
JITDump::DebugInfoRecord debugRecord;
267+
debugRecord.header.timestamp = timestamp;
268+
debugRecord.codeAddress = std::bit_cast<uintptr_t>(executableAddress);
269+
debugRecord.nrEntry = lineEntries.size();
270+
271+
uint32_t totalSize = sizeof(JITDump::DebugInfoRecord);
272+
for (size_t i = 0; i < lineEntries.size(); ++i)
273+
totalSize += sizeof(JITDump::DebugEntry) + (irFilePath.length() + 1);
274+
debugRecord.header.totalSize = totalSize;
275+
276+
logger.write(locker, unsafeMakeSpan(std::bit_cast<char*>(&debugRecord), sizeof(JITDump::DebugInfoRecord)));
277+
278+
for (auto& [codeOffset, lineNumber] : lineEntries) {
279+
JITDump::DebugEntry debugEntry;
280+
debugEntry.codeAddress = std::bit_cast<uintptr_t>(executableAddress) + codeOffset;
281+
debugEntry.line = lineNumber;
282+
debugEntry.discrim = 0;
283+
logger.write(locker, unsafeMakeSpan(std::bit_cast<char*>(&debugEntry), sizeof(JITDump::DebugEntry)));
284+
logger.write(locker, irFilePath.spanIncludingNullTerminator());
285+
}
286+
}
287+
210288
JITDump::CodeLoadRecord record;
211289
record.header.timestamp = timestamp;
212290
record.header.totalSize = sizeof(JITDump::CodeLoadRecord) + (name.length() + 1) + size;

‎Source/JavaScriptCore/assembler/PerfLog.h‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class PerfLog {
4545
WTF_MAKE_NONCOPYABLE(PerfLog);
4646
friend class LazyNeverDestroyed<PerfLog>;
4747
public:
48-
static void log(const CString& name, MacroAssemblerCodeRef<LinkBufferPtrTag>);
48+
static void log(const CString& name, MacroAssemblerCodeRef<LinkBufferPtrTag>, std::unique_ptr<IRDumpDebugInfo>&& = nullptr);
4949

5050
private:
5151
PerfLog();
@@ -56,6 +56,7 @@ class PerfLog {
5656
void flush(const AbstractLocker&) WTF_REQUIRES_LOCK(m_lock);
5757

5858
WTF::FileSystemImpl::FileHandle m_file { };
59+
CString m_irDumpDirectory;
5960
uint64_t m_codeIndex { 0 };
6061
Lock m_lock;
6162
};

‎Source/JavaScriptCore/dfg/DFGGraph.cpp‎

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
#include "GetterSetter.h"
5151
#include "JIT.h"
5252
#include "JSLexicalEnvironment.h"
53+
#include "LinkBuffer.h"
5354
#include "MaxFrameExtentForSlowPathCall.h"
5455
#include "OperandsInlines.h"
5556
#include "ProfilerSupport.h"
@@ -2221,6 +2222,24 @@ void Graph::appendIonGraphPass(const String& passName)
22212222
m_ionGraphPasses->pushObject(pass);
22222223
}
22232224

2225+
UncheckedKeyHashMap<Node*, uint32_t> Graph::collectIRDumpDebugInfo(IRDumpDebugInfo& debugInfo)
2226+
{
2227+
UncheckedKeyHashMap<Node*, uint32_t> nodeToLineIndex;
2228+
for (BlockIndex blockIndex = 0; blockIndex < numBlocks(); ++blockIndex) {
2229+
auto* block = this->block(blockIndex);
2230+
if (!block)
2231+
continue;
2232+
debugInfo.irLines.append({ { }, blockIndex });
2233+
for (size_t i = 0; i < block->size(); ++i) {
2234+
Node* node = block->at(i);
2235+
uint32_t lineIndex = debugInfo.irLines.size();
2236+
nodeToLineIndex.add(node, lineIndex);
2237+
debugInfo.irLines.append({ Graph::opName(node->op()), 0 });
2238+
}
2239+
}
2240+
return nodeToLineIndex;
2241+
}
2242+
22242243
} } // namespace JSC::DFG
22252244

22262245
WTF_ALLOW_UNSAFE_BUFFER_USAGE_END

‎Source/JavaScriptCore/dfg/DFGGraph.h‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ namespace JSC {
5757

5858
class CodeBlock;
5959
class CallFrame;
60+
class IRDumpDebugInfo;
6061

6162
namespace DFG {
6263

@@ -1321,6 +1322,8 @@ class Graph final : public virtual Scannable {
13211322

13221323
RefPtr<JSON::Array> ionGraphPasses() const { return m_ionGraphPasses; }
13231324

1325+
UncheckedKeyHashMap<Node*, uint32_t> collectIRDumpDebugInfo(IRDumpDebugInfo&);
1326+
13241327
StackCheck m_stackChecker;
13251328
VM& m_vm;
13261329
Plan& m_plan;

‎Source/JavaScriptCore/dfg/DFGJITCompiler.cpp‎

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "DFGThunks.h"
4040
#include "JSCJSValueInlines.h"
4141
#include "LinkBuffer.h"
42+
#include "Options.h"
4243
#include "ProbeContext.h"
4344
#include "ThunkGenerators.h"
4445
#include "VM.h"
@@ -379,6 +380,30 @@ void JITCompiler::disassemble(LinkBuffer& linkBuffer)
379380
m_disassembler->reportToProfiler(m_graph.m_plan.compilation(), linkBuffer);
380381
}
381382

383+
void JITCompiler::collectIRDumpDebugInfo(LinkBuffer& linkBuffer)
384+
{
385+
if (!Options::useIRDump())
386+
return;
387+
388+
if (m_irDumpLabels.isEmpty())
389+
return;
390+
391+
auto debugInfo = makeUnique<IRDumpDebugInfo>(m_graph.m_codeBlock->inferredName());
392+
auto nodeToLineIndex = m_graph.collectIRDumpDebugInfo(*debugInfo);
393+
394+
void* codeStart = linkBuffer.entrypoint<DisassemblyPtrTag>().untaggedPtr();
395+
for (auto& entry : m_irDumpLabels) {
396+
auto it = nodeToLineIndex.find(entry.node);
397+
if (it == nodeToLineIndex.end())
398+
continue;
399+
auto location = linkBuffer.locationOf<DisassemblyPtrTag>(entry.label);
400+
uint32_t codeOffset = static_cast<uint32_t>(location.dataLocation<uintptr_t>() - reinterpret_cast<uintptr_t>(codeStart));
401+
debugInfo->codeEntries.append({ codeOffset, it->value });
402+
}
403+
404+
linkBuffer.setIRDumpDebugInfo(WTF::move(debugInfo));
405+
}
406+
382407
#if USE(JSVALUE32_64)
383408
void* JITCompiler::addressOfDoubleConstant(Node* node)
384409
{

‎Source/JavaScriptCore/dfg/DFGJITCompiler.h‎

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ class JITCompiler : public CCallHelpers {
115115

116116
void setForNode(Node* node)
117117
{
118+
if (Options::useIRDump()) [[unlikely]]
119+
m_irDumpLabels.append({ labelIgnoringWatchpoints(), node });
118120
if (!m_disassembler) [[likely]]
119121
return;
120122
m_disassembler->setForNode(node, labelIgnoringWatchpoints());
@@ -385,6 +387,7 @@ class JITCompiler : public CCallHelpers {
385387
void exitSpeculativeWithOSR(const OSRExit&, SpeculationRecovery*);
386388
void linkOSRExits();
387389
void disassemble(LinkBuffer&);
390+
void collectIRDumpDebugInfo(LinkBuffer&);
388391

389392
void makeCatchOSREntryBuffer();
390393

@@ -442,6 +445,12 @@ class JITCompiler : public CCallHelpers {
442445
Vector<ExceptionHandlingOSRExitInfo> m_exceptionHandlerOSRExitCallSites;
443446

444447
PCToCodeOriginMapBuilder m_pcToCodeOriginMapBuilder;
448+
449+
struct IRDumpLabel {
450+
MacroAssembler::Label label;
451+
Node* node;
452+
};
453+
Vector<IRDumpLabel> m_irDumpLabels;
445454
};
446455

447456
} } // namespace JSC::DFG

‎Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,8 @@ void SpeculativeJIT::compile()
182182
link(linkBuffer);
183183
linkOSREntries(linkBuffer);
184184

185+
collectIRDumpDebugInfo(linkBuffer);
186+
185187
disassemble(linkBuffer);
186188

187189
auto codeRef = FINALIZE_DFG_CODE(linkBuffer, JSEntryPtrTag, "DFG JIT code for %s", toCString(CodeBlockWithJITType(m_codeBlock, JITType::DFGJIT)).data());
@@ -285,6 +287,8 @@ void SpeculativeJIT::compileFunction()
285287
link(linkBuffer);
286288
linkOSREntries(linkBuffer);
287289

290+
collectIRDumpDebugInfo(linkBuffer);
291+
288292
disassemble(linkBuffer);
289293

290294
CodePtr<JSEntryPtrTag> withArityCheck = linkBuffer.locationOf<JSEntryPtrTag>(arityCheck);

0 commit comments

Comments
 (0)