Skip to content

Commit debbc1f

Browse files
committed
[ctxprof] Track unhandled call targets
1 parent 35e3208 commit debbc1f

File tree

2 files changed

+38
-6
lines changed

2 files changed

+38
-6
lines changed

compiler-rt/lib/ctx_profile/CtxInstrProfiling.cpp

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -246,22 +246,37 @@ ContextNode *getFlatProfile(FunctionData &Data, GUID Guid,
246246

247247
ContextNode *getUnhandledContext(FunctionData &Data, GUID Guid,
248248
uint32_t NumCounters) {
249-
// 1) if we are under a root (regardless if this thread is collecting or not a
249+
250+
// 1) if we are currently collecting a contextual profile, fetch a ContextNode
251+
// in the `Unhandled` set. We want to do this regardless of `ProfilingStarted`
252+
// to (hopefully) offset the penalty of creating these contexts to before
253+
// profiling.
254+
//
255+
// 2) if we are under a root (regardless if this thread is collecting or not a
250256
// contextual profile for that root), do not collect a flat profile. We want
251257
// to keep flat profiles only for activations that can't happen under a root,
252258
// to avoid confusing profiles. We can, for example, combine flattened and
253259
// flat profiles meaningfully, as we wouldn't double-count anything.
254260
//
255-
// 2) to avoid lengthy startup, don't bother with flat profiles until the
261+
// 3) to avoid lengthy startup, don't bother with flat profiles until the
256262
// profiling started. We would reset them anyway when profiling starts.
257263
// HOWEVER. This does lose profiling for message pumps: those functions are
258264
// entered once and never exit. They should be assumed to be entered before
259265
// profiling starts - because profiling should start after the server is up
260266
// and running (which is equivalent to "message pumps are set up").
261-
if (IsUnderContext || !__sanitizer::atomic_load_relaxed(&ProfilingStarted))
262-
return TheScratchContext;
263-
return markAsScratch(
264-
onContextEnter(*getFlatProfile(Data, Guid, NumCounters)));
267+
ContextRoot *R = __llvm_ctx_profile_current_context_root;
268+
if (!R) {
269+
if (IsUnderContext || !__sanitizer::atomic_load_relaxed(&ProfilingStarted))
270+
return TheScratchContext;
271+
else
272+
return markAsScratch(
273+
onContextEnter(*getFlatProfile(Data, Guid, NumCounters)));
274+
}
275+
auto It = R->Unhandled.insert({Guid, nullptr});
276+
if (It.second)
277+
It.first->second =
278+
getCallsiteSlow(Guid, &R->FirstUnhandledCalleeNode, NumCounters, 0);
279+
return markAsScratch(onContextEnter(*It.first->second));
265280
}
266281

267282
ContextNode *__llvm_ctx_profile_get_context(FunctionData *Data, void *Callee,
@@ -396,6 +411,8 @@ void __llvm_ctx_profile_start_collection() {
396411
++NumMemUnits;
397412

398413
resetContextNode(*Root->FirstNode);
414+
if (Root->FirstUnhandledCalleeNode)
415+
resetContextNode(*Root->FirstUnhandledCalleeNode);
399416
__sanitizer::atomic_store_relaxed(&Root->TotalEntries, 0);
400417
}
401418
__sanitizer::atomic_store_relaxed(&ProfilingStarted, true);

compiler-rt/lib/ctx_profile/CtxInstrProfiling.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#define CTX_PROFILE_CTXINSTRPROFILING_H_
1111

1212
#include "CtxInstrContextNode.h"
13+
#include "sanitizer_common/sanitizer_dense_map.h"
1314
#include "sanitizer_common/sanitizer_mutex.h"
1415
#include <sanitizer/common_interface_defs.h>
1516

@@ -83,6 +84,20 @@ struct ContextRoot {
8384
// Count the number of entries - regardless if we could take the `Taken` mutex
8485
::__sanitizer::atomic_uint64_t TotalEntries = {};
8586

87+
// Profiles for functions we encounter when collecting a contexutal profile,
88+
// that are not associated with a callsite. This is expected to happen for
89+
// signal handlers, but it also - problematically - currently happens for
90+
// mem{memset|copy|move|set}, which are currently inserted after profile
91+
// instrumentation.
92+
// `Unhandled` serves 2 purposes:
93+
// 1. identifying such cases (like the memops)
94+
// 2. collecting a profile for them, which can be at least used as a flat
95+
// profile
96+
::__sanitizer::DenseMap<GUID, ContextNode *> Unhandled;
97+
// Keep the unhandled contexts in a list, as we allocate them, as it makes it
98+
// simpler to send to the writer when the profile is fetched.
99+
ContextNode *FirstUnhandledCalleeNode = nullptr;
100+
86101
// Taken is used to ensure only one thread traverses the contextual graph -
87102
// either to read it or to write it. On server side, the same entrypoint will
88103
// be entered by numerous threads, but over time, the profile aggregated by

0 commit comments

Comments
 (0)