Skip to content

Commit

Permalink
[JSC] Update RegExp tracing code
Browse files Browse the repository at this point in the history
https://bugs.webkit.org/show_bug.cgi?id=271625
rdar://125334427

Reviewed by Yusuke Suzuki.

Moved the tracing output header generation from VM.cpp to RegExp.cpp to is same place as where the code to format the trace entries.
Parameterized the formatting code to use a width value.  Fixed the dumping of trace data to work in the jsc shell when
processing modules.  Eliminated use of snprintf() in favor of makeString() calls.  Some other minor changes made as well.

This code is build time enabled through ENABLE_REGEXP_TRACING, which is by default turned off.

* Source/JavaScriptCore/jsc.cpp:
(runJSC):
* Source/JavaScriptCore/runtime/RegExp.cpp:
(JSC::RegExp::printTraceHeader):
(JSC::RegExp::printTraceData):
* Source/JavaScriptCore/runtime/RegExp.h:
* Source/JavaScriptCore/runtime/VM.cpp:
(JSC::VM::dumpRegExpTrace):
* Source/JavaScriptCore/runtime/VM.h:

Canonical link: https://commits.webkit.org/276730@main
  • Loading branch information
msaboff committed Mar 27, 2024
1 parent ef3fe7c commit 6299ee9
Show file tree
Hide file tree
Showing 5 changed files with 140 additions and 46 deletions.
8 changes: 8 additions & 0 deletions Source/JavaScriptCore/jsc.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4215,6 +4215,14 @@ int runJSC(const CommandLine& options, bool isWorker, const Func& func)
fprintf(stderr, "could not save profiler output.\n");
}


#if ENABLE(REGEXP_TRACING)
{
JSLockHolder locker(vm);
vm.dumpRegExpTrace();
}
#endif

#if ENABLE(JIT)
{
JSLockHolder locker(vm);
Expand Down
163 changes: 123 additions & 40 deletions Source/JavaScriptCore/runtime/RegExp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
#include "RegExpInlines.h"
#include "YarrJIT.h"
#include <wtf/Assertions.h>
#include <wtf/DataLog.h>
#include <wtf/text/StringConcatenate.h>

namespace JSC {

Expand Down Expand Up @@ -420,55 +422,136 @@ void RegExp::matchCompareWithInterpreter(const String& s, int startOffset, int*
#endif

#if ENABLE(REGEXP_TRACING)
void RegExp::printTraceData()
{
char formattedPattern[41];
char rawPattern[41];
void RegExp::printTraceHeader()
{
dataLogF("\nRegExp Tracing\n");
dataLogF("Regular Expression");
for (unsigned i = 0; i < SameLineFormatedRegExpnWidth - 16; ++i)
dataLogF(" ");
dataLogF(" 8 Bit 16 Bit match() Matches Average\n");
dataLogF(" <Match only / Match>");
for (unsigned i = 0; i < RegExp::SameLineFormatedRegExpnWidth - 21; ++i)
dataLogF(" ");
dataLogF(" JIT Addr JIT Addr calls found String len\n");
for (unsigned i = 0; i < RegExp::SameLineFormatedRegExpnWidth; ++i)
dataLogF("-");

dataLogF("+------------+------------+----------+----------+-----------\n");
}

strncpy(rawPattern, pattern().utf8().data(), 40);
rawPattern[40]= '\0';
void RegExp::printTraceData()
{
char formattedRegExp[SameLineFormatedRegExpnWidth + 1];
char rawPatternBuffer[SameLineFormatedRegExpnWidth + 1];
String rawPattern;

int pattLen = strlen(rawPattern);
memset(formattedRegExp, ' ', SameLineFormatedRegExpnWidth);
formattedRegExp[SameLineFormatedRegExpnWidth] = '\0';

snprintf(formattedPattern, 41, (pattLen <= 38) ? "/%.38s/" : "/%.36s...", rawPattern);
auto patternCStr = pattern().utf8(); // Hold a reference so it doesn't get destroyed.
auto patternStr = patternCStr.data();
auto patternLength = pattern().length();

#if ENABLE(YARR_JIT)
const size_t jitAddrSize = 20;
char jit8BitMatchOnlyAddr[jitAddrSize] { };
char jit16BitMatchOnlyAddr[jitAddrSize] { };
char jit8BitMatchAddr[jitAddrSize] { };
char jit16BitMatchAddr[jitAddrSize] { };
switch (m_state) {
case ParseError:
case NotCompiled:
break;
case ByteCode:
snprintf(jit8BitMatchOnlyAddr, jitAddrSize, "fallback ");
snprintf(jit16BitMatchOnlyAddr, jitAddrSize, "---- ");
snprintf(jit8BitMatchAddr, jitAddrSize, "fallback ");
snprintf(jit16BitMatchAddr, jitAddrSize, "---- ");
break;
case JITCode: {
Yarr::YarrCodeBlock& codeBlock = *m_regExpJITCode.get();
snprintf(jit8BitMatchOnlyAddr, jitAddrSize, "0x%014" PRIxPTR, reinterpret_cast<uintptr_t>(codeBlock.get8BitMatchOnlyAddr()));
snprintf(jit16BitMatchOnlyAddr, jitAddrSize, "0x%014" PRIxPTR, reinterpret_cast<uintptr_t>(codeBlock.get16BitMatchOnlyAddr()));
snprintf(jit8BitMatchAddr, jitAddrSize, "0x%014" PRIxPTR, reinterpret_cast<uintptr_t>(codeBlock.get8BitMatchAddr()));
snprintf(jit16BitMatchAddr, jitAddrSize, "0x%014" PRIxPTR, reinterpret_cast<uintptr_t>(codeBlock.get16BitMatchAddr()));
break;
auto appendRawPatternBuffer = [&] (size_t& index) {
if (!index)
return;

rawPatternBuffer[index] = '\0';

rawPattern = makeString(rawPattern, rawPatternBuffer);

index = 0;
};

// Escape literal TAB characters.
size_t dstIdx = 0;
for (size_t srcIdx = 0; srcIdx < patternLength; ++srcIdx) {
auto c = patternStr[srcIdx];
if (c == '\t') {
rawPatternBuffer[dstIdx++] = '\\';
if (dstIdx >= SameLineFormatedRegExpnWidth)
appendRawPatternBuffer(dstIdx);
c = 't';
}

rawPatternBuffer[dstIdx++] = c;
if (dstIdx >= SameLineFormatedRegExpnWidth)
appendRawPatternBuffer(dstIdx);
}

appendRawPatternBuffer(dstIdx);

if (rawPattern.length() + strlen(Yarr::flagsString(flags()).data()) + 2 <= SameLineFormatedRegExpnWidth) {
String result = makeString('/', rawPattern, '/', Yarr::flagsString(flags()).data());
memcpy(formattedRegExp, result.utf8().data(), result.length());
formattedRegExp[result.length()] = '\0';
} else
dataLogF("/%s/%s\n", rawPattern.utf8().data(), Yarr::flagsString(flags()).data());

constexpr int addrWidth = 12;
#if ENABLE(YARR_JIT)
constexpr char hexDigits[] = "0123456789abcdef";
constexpr char fallback[] = "fallback ";
constexpr char dashes[] = "---- ";

String jit8BitMatchOnlyAddr;
String jit16BitMatchOnlyAddr;
String jit8BitMatchAddr;
String jit16BitMatchAddr;

auto formatAddress = [&] (void* addr) {
constexpr int jitAddrSignificantWidth = addrWidth - 2;
uintptr_t addrAsUint = reinterpret_cast<uintptr_t>(addr);

String formatResult;
for (int digit = jitAddrSignificantWidth; digit; --digit) {
auto nibble = (addrAsUint >> ((digit - 1) * 4)) & 0xf;
if (!formatResult.length()) {
if (!nibble && digit > 8)
continue;

formatResult = makeString("0x");
}
formatResult = makeString(formatResult, hexDigits[nibble]);
}

return formatResult;
};

switch (m_state) {
case ParseError:
case NotCompiled:
break;
case ByteCode:
jit8BitMatchOnlyAddr = makeString(fallback);
jit16BitMatchOnlyAddr = makeString(dashes);
jit8BitMatchAddr = makeString(fallback);
jit16BitMatchAddr = makeString(dashes);
break;
case JITCode: {
Yarr::YarrCodeBlock& codeBlock = *m_regExpJITCode.get();
jit8BitMatchOnlyAddr = formatAddress(codeBlock.get8BitMatchOnlyAddr());
jit16BitMatchOnlyAddr = formatAddress(codeBlock.get16BitMatchOnlyAddr());
jit8BitMatchAddr = formatAddress(codeBlock.get8BitMatchAddr());
jit16BitMatchAddr = formatAddress(codeBlock.get16BitMatchAddr());
break;
}
}
#else
const char* jit8BitMatchOnlyAddr = "JIT Off";
const char* jit16BitMatchOnlyAddr = "";
const char* jit8BitMatchAddr = "JIT Off";
const char* jit16BitMatchAddr = "";
constexpr char jitOff[] = "JIT Off";
String jit8BitMatchOnlyAddr = makeString(jitOff);
String jit16BitMatchOnlyAddr;
String jit8BitMatchAddr = makeString(jitOff);
String jit16BitMatchAddr;
#endif
unsigned averageMatchOnlyStringLen = (unsigned)(m_rtMatchOnlyTotalSubjectStringLen / m_rtMatchOnlyCallCount);
unsigned averageMatchStringLen = (unsigned)(m_rtMatchTotalSubjectStringLen / m_rtMatchCallCount);
unsigned averageMatchOnlyStringLen = (unsigned)(m_rtMatchOnlyTotalSubjectStringLen / m_rtMatchOnlyCallCount);
unsigned averageMatchStringLen = (unsigned)(m_rtMatchTotalSubjectStringLen / m_rtMatchCallCount);

printf("%-40.40s %16.16s %16.16s %10d %10d %10u\n", formattedPattern, jit8BitMatchOnlyAddr, jit16BitMatchOnlyAddr, m_rtMatchOnlyCallCount, m_rtMatchOnlyFoundCount, averageMatchOnlyStringLen);
printf(" %16.16s %16.16s %10d %10d %10u\n", jit8BitMatchAddr, jit16BitMatchAddr, m_rtMatchCallCount, m_rtMatchFoundCount, averageMatchStringLen);
}
dataLogF("%-*.*s %*.*s %*.*s %10d %10d %10u\n", SameLineFormatedRegExpnWidth, SameLineFormatedRegExpnWidth, formattedRegExp, addrWidth, addrWidth, jit8BitMatchOnlyAddr.utf8().data(), addrWidth, addrWidth, jit16BitMatchOnlyAddr.utf8().data(), m_rtMatchOnlyCallCount, m_rtMatchOnlyFoundCount, averageMatchOnlyStringLen);
for (unsigned i = 0; i < SameLineFormatedRegExpnWidth; ++i)
dataLog(" ");
dataLogF(" %*.*s %*.*s %10d %10d %10u\n", addrWidth, addrWidth, jit8BitMatchAddr.utf8().data(), addrWidth, addrWidth, jit16BitMatchAddr.utf8().data(), m_rtMatchCallCount, m_rtMatchFoundCount, averageMatchStringLen);
}
#endif

void RegExp::dumpToStream(const JSCell* cell, PrintStream& out)
Expand Down
2 changes: 2 additions & 0 deletions Source/JavaScriptCore/runtime/RegExp.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ class RegExp final : public JSCell {
void deleteCode();

#if ENABLE(REGEXP_TRACING)
constexpr static unsigned SameLineFormatedRegExpnWidth = 74;
static void printTraceHeader();
void printTraceData();
#endif

Expand Down
10 changes: 5 additions & 5 deletions Source/JavaScriptCore/runtime/VM.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1176,15 +1176,15 @@ void VM::addRegExpToTrace(RegExp* regExp)

void VM::dumpRegExpTrace()
{
if (m_rtTraceList.size() <= 1)
return;

// The first RegExp object is ignored. It is created by the RegExpPrototype ctor and not used.
RTTraceList::iterator iter = ++m_rtTraceList.begin();

if (iter != m_rtTraceList.end()) {
dataLogF("\nRegExp Tracing\n");
dataLogF("Regular Expression 8 Bit 16 Bit match() Matches Average\n");
dataLogF(" <Match only / Match> JIT Addr JIT Address calls found String len\n");
dataLogF("----------------------------------------+----------------+----------------+----------+----------+-----------\n");

RegExp::printTraceHeader();

unsigned reCount = 0;

for (; iter != m_rtTraceList.end(); ++iter, ++reCount) {
Expand Down
3 changes: 2 additions & 1 deletion Source/JavaScriptCore/runtime/VM.h
Original file line number Diff line number Diff line change
Expand Up @@ -860,7 +860,8 @@ class VM : public ThreadSafeRefCounted<VM>, public DoublyLinkedListNode<VM> {
JS_EXPORT_PRIVATE void invalidateStructureChainIntegrity(StructureChainIntegrityEvent);

#if ENABLE(REGEXP_TRACING)
ListHashSet<RegExp*> m_rtTraceList;
using RTTraceList = ListHashSet<RegExp*>;
RTTraceList m_rtTraceList;
void addRegExpToTrace(RegExp*);
JS_EXPORT_PRIVATE void dumpRegExpTrace();
#endif
Expand Down

0 comments on commit 6299ee9

Please sign in to comment.