[llvm-branch-commits] [compiler-rt] [ctxprof] Track unhandled call targets (PR #131417)
Mircea Trofin via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Mar 14 22:32:24 PDT 2025
https://github.com/mtrofin updated https://github.com/llvm/llvm-project/pull/131417
>From 5916e7d345d84368b18cc08e32e2c63e4b967ec3 Mon Sep 17 00:00:00 2001
From: Mircea Trofin <mtrofin at google.com>
Date: Fri, 14 Mar 2025 15:59:22 -0700
Subject: [PATCH] [ctxprof] Track unhandled call targets
---
.../lib/ctx_profile/CtxInstrProfiling.cpp | 29 +++++++++++++++----
.../lib/ctx_profile/CtxInstrProfiling.h | 20 +++++++++++--
2 files changed, 40 insertions(+), 9 deletions(-)
diff --git a/compiler-rt/lib/ctx_profile/CtxInstrProfiling.cpp b/compiler-rt/lib/ctx_profile/CtxInstrProfiling.cpp
index 6ef7076d93e31..49b5e8167fb95 100644
--- a/compiler-rt/lib/ctx_profile/CtxInstrProfiling.cpp
+++ b/compiler-rt/lib/ctx_profile/CtxInstrProfiling.cpp
@@ -246,22 +246,37 @@ ContextNode *getFlatProfile(FunctionData &Data, GUID Guid,
ContextNode *getUnhandledContext(FunctionData &Data, GUID Guid,
uint32_t NumCounters) {
- // 1) if we are under a root (regardless if this thread is collecting or not a
+
+ // 1) if we are currently collecting a contextual profile, fetch a ContextNode
+ // in the `Unhandled` set. We want to do this regardless of `ProfilingStarted`
+ // to (hopefully) offset the penalty of creating these contexts to before
+ // profiling.
+ //
+ // 2) if we are under a root (regardless if this thread is collecting or not a
// contextual profile for that root), do not collect a flat profile. We want
// to keep flat profiles only for activations that can't happen under a root,
// to avoid confusing profiles. We can, for example, combine flattened and
// flat profiles meaningfully, as we wouldn't double-count anything.
//
- // 2) to avoid lengthy startup, don't bother with flat profiles until the
+ // 3) to avoid lengthy startup, don't bother with flat profiles until the
// profiling started. We would reset them anyway when profiling starts.
// HOWEVER. This does lose profiling for message pumps: those functions are
// entered once and never exit. They should be assumed to be entered before
// profiling starts - because profiling should start after the server is up
// and running (which is equivalent to "message pumps are set up").
- if (IsUnderContext || !__sanitizer::atomic_load_relaxed(&ProfilingStarted))
- return TheScratchContext;
- return markAsScratch(
- onContextEnter(*getFlatProfile(Data, Guid, NumCounters)));
+ ContextRoot *R = __llvm_ctx_profile_current_context_root;
+ if (!R) {
+ if (IsUnderContext || !__sanitizer::atomic_load_relaxed(&ProfilingStarted))
+ return TheScratchContext;
+ else
+ return markAsScratch(
+ onContextEnter(*getFlatProfile(Data, Guid, NumCounters)));
+ }
+ auto It = R->Unhandled.insert({Guid, nullptr});
+ if (It.second)
+ It.first->second =
+ getCallsiteSlow(Guid, &R->FirstUnhandledCalleeNode, NumCounters, 0);
+ return markAsScratch(onContextEnter(*It.first->second));
}
ContextNode *__llvm_ctx_profile_get_context(FunctionData *Data, void *Callee,
@@ -396,6 +411,8 @@ void __llvm_ctx_profile_start_collection() {
++NumMemUnits;
resetContextNode(*Root->FirstNode);
+ if (Root->FirstUnhandledCalleeNode)
+ resetContextNode(*Root->FirstUnhandledCalleeNode);
__sanitizer::atomic_store_relaxed(&Root->TotalEntries, 0);
}
__sanitizer::atomic_store_relaxed(&ProfilingStarted, true);
diff --git a/compiler-rt/lib/ctx_profile/CtxInstrProfiling.h b/compiler-rt/lib/ctx_profile/CtxInstrProfiling.h
index 6bb954da950c4..5478a3b67ac25 100644
--- a/compiler-rt/lib/ctx_profile/CtxInstrProfiling.h
+++ b/compiler-rt/lib/ctx_profile/CtxInstrProfiling.h
@@ -10,6 +10,7 @@
#define CTX_PROFILE_CTXINSTRPROFILING_H_
#include "CtxInstrContextNode.h"
+#include "sanitizer_common/sanitizer_dense_map.h"
#include "sanitizer_common/sanitizer_mutex.h"
#include <sanitizer/common_interface_defs.h>
@@ -73,9 +74,8 @@ static_assert(alignof(Arena) == ExpectedAlignment);
// sure the inlined vectors are appropriately aligned.
static_assert(alignof(ContextNode) == ExpectedAlignment);
-/// ContextRoots are allocated by LLVM for entrypoints. LLVM is only concerned
-/// with allocating and zero-initializing the global value (as in, GlobalValue)
-/// for it.
+/// ContextRoots hold memory and the start of the contextual profile tree for a
+/// root function.
struct ContextRoot {
ContextNode *FirstNode = nullptr;
Arena *FirstMemBlock = nullptr;
@@ -84,6 +84,20 @@ struct ContextRoot {
// Count the number of entries - regardless if we could take the `Taken` mutex
::__sanitizer::atomic_uint64_t TotalEntries = {};
+ // Profiles for functions we encounter when collecting a contexutal profile,
+ // that are not associated with a callsite. This is expected to happen for
+ // signal handlers, but it also - problematically - currently happens for
+ // mem{memset|copy|move|set}, which are currently inserted after profile
+ // instrumentation.
+ // `Unhandled` serves 2 purposes:
+ // 1. identifying such cases (like the memops)
+ // 2. collecting a profile for them, which can be at least used as a flat
+ // profile
+ ::__sanitizer::DenseMap<GUID, ContextNode *> Unhandled;
+ // Keep the unhandled contexts in a list, as we allocate them, as it makes it
+ // simpler to send to the writer when the profile is fetched.
+ ContextNode *FirstUnhandledCalleeNode = nullptr;
+
// Taken is used to ensure only one thread traverses the contextual graph -
// either to read it or to write it. On server side, the same entrypoint will
// be entered by numerous threads, but over time, the profile aggregated by
More information about the llvm-branch-commits
mailing list