Skip to content

Commit

Permalink
Add native function names to the profile
Browse files Browse the repository at this point in the history
Summary: Outputs Host&Native function names for functions other than internal hermes functions.

Reviewed By: neildhar

Differential Revision: D37893556

fbshipit-source-id: 799aa9680477964cd3d3b7c5a7f154127a2cf434
  • Loading branch information
jpporto authored and facebook-github-bot committed Aug 11, 2022
1 parent 21a76bb commit a8236b8
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 58 deletions.
6 changes: 5 additions & 1 deletion include/hermes/VM/Profiler/ChromeTraceSerializerPosix.h
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ class ChromeTraceFormat {
/// Serialize input ChromeTraceFormat to output stream.
class ChromeTraceSerializer {
private:
const SamplingProfiler &samplingProfiler_;
ChromeTraceFormat trace_;
SamplingProfiler::TimeStampType firstEventTimeStamp_;

Expand All @@ -218,7 +219,9 @@ class ChromeTraceSerializer {
SamplingProfiler::TimeStampType timeStamp);

public:
explicit ChromeTraceSerializer(ChromeTraceFormat &&chromeTrace);
explicit ChromeTraceSerializer(
const SamplingProfiler &sp,
ChromeTraceFormat &&chromeTrace);

/// Serialize chrome trace to \p OS.
void serialize(llvh::raw_ostream &OS) const;
Expand All @@ -230,6 +233,7 @@ class ChromeTraceSerializer {
/// https://chromedevtools.github.io/devtools-protocol/tot/Profiler/#type-Profile
///
void serializeAsProfilerProfile(
const SamplingProfiler &sp,
llvh::raw_ostream &os,
ChromeTraceFormat &&chromeTrace);

Expand Down
62 changes: 51 additions & 11 deletions include/hermes/VM/Profiler/SamplingProfilerPosix.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "hermes/Support/Semaphore.h"
#include "hermes/Support/ThreadLocal.h"
#include "hermes/VM/Callable.h"
#include "hermes/VM/JSNativeFunctions.h"
#include "hermes/VM/Runtime.h"

#include "llvh/ADT/DenseMap.h"
Expand Down Expand Up @@ -61,9 +62,11 @@ class SamplingProfiler {
uint32_t offset;
};
/// Captured NativeFunction frame information for symbolication.
using NativeFunctionFrameInfo = NativeFunctionPtr;
/// Captured FinalizableNativeFunction frame information for symbolication.
using FinalizableNativeFunctionFrameInfo = NativeFunctionPtr;
using LoomNativeFrameInfo = NativeFunctionPtr;
/// Captured NativeFunction frame information for symbolication that hasn't
/// been registered with the sampling profiler yet, and therefore can be moved
/// by the GC.
using NativeFunctionFrameInfo = size_t;
/// GC frame info. Pointing to string in suspendEventExtraInfoSet_.
using SuspendFrameInfo = const std::string *;

Expand All @@ -84,10 +87,10 @@ class SamplingProfiler {
union {
/// Pure JS function frame info.
JSFunctionFrameInfo jsFrame;
/// Native function frame info.
/// Native function frame info storage used for loom profiling.
LoomNativeFrameInfo nativeFunctionPtrForLoom;
/// Native function frame info storage used for "regular" profiling.
NativeFunctionFrameInfo nativeFrame;
/// Host function frame info.
FinalizableNativeFunctionFrameInfo finalizableNativeFrame;
/// Suspend frame info. Pointing to string
/// in suspendExtraInfoSet_; it is optional and
/// can be null to indicate no extra info.
Expand Down Expand Up @@ -122,6 +125,35 @@ class SamplingProfiler {
}
#endif // UNIT_TEST

/// \returns the NativeFunctionPtr for \p stackFrame. Caller must hold
/// runtimeDataLock_.
NativeFunctionPtr getNativeFunctionPtr(const StackFrame &stackFrame) const {
assert(
stackFrame.kind == StackFrame::FrameKind::NativeFunction &&
"unexpected stack frame kind");
return nativeFunctions_[stackFrame.nativeFrame]->getFunctionPtr();
}

/// \returns the name (if one exists) for \p stackFrame. Caller must hold
/// runtimeDataLock_.
std::string getNativeFunctionName(const StackFrame &stackFrame) const {
if (stackFrame.kind == StackFrame::FrameKind::NativeFunction) {
// FrameKing::NativeFunction frames may be JS functions that are internal
// to hermes -- e.g., Array.prototype.sort. For those functions, return
// the native function name as defined in NativeFunctions.def.
const char *name =
hermes::vm::getFunctionName(getNativeFunctionPtr(stackFrame));
if (strcmp(name, "")) {
return name;
}
}

// For all other FrameKind::NativeFunction frames, as well as
// FrameKing::FinalizableNativeFunction ones, return the native function
// name attribute, if one is available.
return nativeFunctions_[stackFrame.nativeFrame]->getNameIfExists(runtime_);
}

private:
/// Max size of sampleStorage_.
static const int kMaxStackDepth = 500;
Expand Down Expand Up @@ -262,6 +294,10 @@ class SamplingProfiler {
/// runtimeDataLock_.
std::vector<Domain *> domains_;

/// NativeFunctions to be kept alive for sampled NativeFunctionFrameInfo.
/// Protected by runtimeDataLock_.
std::vector<NativeFunction *> nativeFunctions_;

Runtime &runtime_;

private:
Expand All @@ -270,22 +306,26 @@ class SamplingProfiler {
/// Refer to Domain.h for relationship between Domain and RuntimeModule.
void registerDomain(Domain *domain);

enum class SaveDomains { No, Yes };
/// Hold \p nativeFunction so native function names can be added to the stack
/// traces.
NativeFunctionFrameInfo registerNativeFunction(
NativeFunction *nativeFunction);

enum class InLoom { No, Yes };

/// Walk runtime stack frames and store in \p sampleStorage.
/// This function is called from signal handler so should obey all
/// rules of signal handler(no lock, no memory allocation etc...)
/// \param startIndex specifies the start index in \p sampleStorage to fill.
/// \param saveDomains specifies whether domains should be registered, so that
/// they are available when dumping a trace.
/// \param inLoom specifies this function is being invoked in a Loom callback.
/// \return total number of stack frames captured in \p sampleStorage
/// including existing frames before \p startIndex.
uint32_t walkRuntimeStack(
StackTrace &sampleStorage,
SaveDomains saveDomains,
InLoom inLoom,
uint32_t startIndex = 0);

/// Record JS stack at time of suspension, , caller must hold
/// Record JS stack at time of suspension, caller must hold
/// runtimeDataLock_.
void recordPreSuspendStack(std::string_view extraInfo);

Expand Down
40 changes: 25 additions & 15 deletions lib/VM/Profiler/ChromeTraceSerializerPosix.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,10 @@ OptValue<hbc::DebugSourceLocation> getSourceLocation(
}
} // namespace

ChromeTraceSerializer::ChromeTraceSerializer(ChromeTraceFormat &&chromeTrace)
: trace_(std::move(chromeTrace)) {
ChromeTraceSerializer::ChromeTraceSerializer(
const SamplingProfiler &sp,
ChromeTraceFormat &&chromeTrace)
: samplingProfiler_(sp), trace_(std::move(chromeTrace)) {
firstEventTimeStamp_ = trace_.getSampledEvents().empty()
? std::chrono::steady_clock::now()
: trace_.getSampledEvents()[0].getTimeStamp();
Expand Down Expand Up @@ -166,7 +168,7 @@ void ChromeTraceSerializer::serializeSampledEvents(JSONEmitter &json) const {
}

void ChromeTraceSerializer::serializeStackFrames(JSONEmitter &json) const {
trace_.getRoot().dfsWalk([&json](
trace_.getRoot().dfsWalk([&samplingProfiler = samplingProfiler_, &json](
const ChromeStackFrameNode &node,
const ChromeStackFrameNode *parent) {
json.emitKey(std::to_string(node.getId()));
Expand Down Expand Up @@ -232,16 +234,15 @@ void ChromeTraceSerializer::serializeStackFrames(JSONEmitter &json) const {
}

case SamplingProfiler::StackFrame::FrameKind::NativeFunction: {
frameName =
std::string("[Native] ") + getFunctionName(frame.nativeFrame);
frameName = std::string("[Native] ") +
samplingProfiler.getNativeFunctionName(frame);
categoryName = "Native";
break;
}

case SamplingProfiler::StackFrame::FrameKind::FinalizableNativeFunction: {
// TODO: find a way to get host function name out of
// FinalizableNativeFunction.
frameName = "[HostFunction]";
frameName = std::string("[HostFunction] ") +
samplingProfiler.getNativeFunctionName(frame);
categoryName = "Native";
break;
}
Expand Down Expand Up @@ -319,19 +320,24 @@ class ProfilerProfileSerializer {

public:
ProfilerProfileSerializer(
const SamplingProfiler &sp,
JSONEmitter &emitter,
ChromeTraceFormat &&chromeTrace)
: json_(emitter), chromeTrace_(std::move(chromeTrace)) {}
: samplingProfiler_(sp),
json_(emitter),
chromeTrace_(std::move(chromeTrace)) {}

void serialize() const;

private:
void processNode(const ChromeStackFrameNode &node) const;
void emitNodes() const;
void emitStartTime() const;
void emitEndTime() const;
void emitSamples() const;
void emitTimeDeltas() const;

const SamplingProfiler &samplingProfiler_;
JSONEmitter &json_;
ChromeTraceFormat chromeTrace_;
};
Expand Down Expand Up @@ -395,7 +401,8 @@ static void emitProfileNode(
json.closeDict(); // node
}

static void processNode(JSONEmitter &json, const ChromeStackFrameNode &node) {
void ProfilerProfileSerializer::processNode(
const ChromeStackFrameNode &node) const {
std::string name;
std::string url;
uint32_t scriptId = 0;
Expand Down Expand Up @@ -427,13 +434,15 @@ static void processNode(JSONEmitter &json, const ChromeStackFrameNode &node) {
}

case SamplingProfiler::StackFrame::FrameKind::NativeFunction: {
name = std::string("[Native] ") + getFunctionName(frame.nativeFrame);
name = std::string("[Native] ") +
samplingProfiler_.getNativeFunctionName(frame);
url = "[native]";
break;
}

case SamplingProfiler::StackFrame::FrameKind::FinalizableNativeFunction: {
name = "[Host Function]";
name = std::string("[Host Function] ") +
samplingProfiler_.getNativeFunctionName(frame);
url = "[host]";
break;
}
Expand All @@ -449,7 +458,7 @@ static void processNode(JSONEmitter &json, const ChromeStackFrameNode &node) {
llvm_unreachable("Unknown frame kind");
}

emitProfileNode(json, node, name, scriptId, url, lineNumber, columnNumber);
emitProfileNode(json_, node, name, scriptId, url, lineNumber, columnNumber);
}

void ProfilerProfileSerializer::emitNodes() const {
Expand All @@ -467,7 +476,7 @@ void ProfilerProfileSerializer::emitNodes() const {
if (&node == root) {
assert(!parent && "root node should not have parent");
} else {
processNode(json_, node);
processNode(node);
}
});

Expand Down Expand Up @@ -539,11 +548,12 @@ void ProfilerProfileSerializer::emitTimeDeltas() const {
} // namespace

void serializeAsProfilerProfile(
const SamplingProfiler &sp,
llvh::raw_ostream &os,
ChromeTraceFormat &&chromeTrace) {
JSONEmitter json(os);

ProfilerProfileSerializer s(json, std::move(chromeTrace));
ProfilerProfileSerializer s(sp, json, std::move(chromeTrace));
s.serialize();
}
} // namespace vm
Expand Down
Loading

0 comments on commit a8236b8

Please sign in to comment.