Skip to content

[PassManager] Add pretty stack frames #96078

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions llvm/include/llvm/IR/PassInstrumentation.h
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,13 @@ class PassInstrumentation {
if (Callbacks)
Callbacks->BeforeNonSkippedPassCallbacks.pop_back();
}

/// Get the pass name for a given pass class name.
StringRef getPassNameForClassName(StringRef ClassName) const {
if (Callbacks)
return Callbacks->getPassNameForClassName(ClassName);
return {};
}
};

bool isSpecialPass(StringRef PassID, const std::vector<StringRef> &Specials);
Expand Down
10 changes: 10 additions & 0 deletions llvm/include/llvm/IR/PassManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -224,11 +224,21 @@ class PassManager : public PassInfoMixin<
std::vector<std::unique_ptr<PassConceptT>> Passes;
};

template <typename IRUnitT>
void printIRUnitNameForStackTrace(raw_ostream &OS, const IRUnitT &IR);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we put these in PassManagerImpl.h?

Copy link
Contributor Author

@nikic nikic Jun 24, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In theory, no, because other places using PassManagerImpl.h also need to specialize these for other types. In practice, yes, we could, because all non-Module/Function users actually specialize the PassManager::run implementation (and as such currently aren't part of the stack trace).

Architecturally, I think putting them in PassManager.h is correct, though possibly this whole implementation is unnecessarily general given that we only end up using it for Module/Function right now.


template <>
void printIRUnitNameForStackTrace<Module>(raw_ostream &OS, const Module &IR);

extern template class PassManager<Module>;

/// Convenience typedef for a pass manager over modules.
using ModulePassManager = PassManager<Module>;

template <>
void printIRUnitNameForStackTrace<Function>(raw_ostream &OS,
const Function &IR);

extern template class PassManager<Function>;

/// Convenience typedef for a pass manager over functions.
Expand Down
26 changes: 25 additions & 1 deletion llvm/include/llvm/IR/PassManagerImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@
#ifndef LLVM_IR_PASSMANAGERIMPL_H
#define LLVM_IR_PASSMANAGERIMPL_H

#include "llvm/Support/CommandLine.h"
#include "llvm/IR/PassInstrumentation.h"
#include "llvm/IR/PassManager.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/PrettyStackTrace.h"

extern llvm::cl::opt<bool> UseNewDbgInfoFormat;

Expand All @@ -26,6 +27,28 @@ namespace llvm {
template <typename IRUnitT, typename AnalysisManagerT, typename... ExtraArgTs>
PreservedAnalyses PassManager<IRUnitT, AnalysisManagerT, ExtraArgTs...>::run(
IRUnitT &IR, AnalysisManagerT &AM, ExtraArgTs... ExtraArgs) {
class StackTraceEntry : public PrettyStackTraceEntry {
const PassInstrumentation &PI;
PassConceptT &Pass;
IRUnitT &IR;

public:
explicit StackTraceEntry(const PassInstrumentation &PI, PassConceptT &Pass,
IRUnitT &IR)
: PI(PI), Pass(Pass), IR(IR) {}

void print(raw_ostream &OS) const override {
OS << "Running pass \"";
Pass.printPipeline(OS, [this](StringRef ClassName) {
auto PassName = PI.getPassNameForClassName(ClassName);
return PassName.empty() ? ClassName : PassName;
});
OS << "\" on ";
printIRUnitNameForStackTrace(OS, IR);
OS << "\n";
}
};

PreservedAnalyses PA = PreservedAnalyses::all();

// Request PassInstrumentation from analysis manager, will use it to run
Expand All @@ -47,6 +70,7 @@ PreservedAnalyses PassManager<IRUnitT, AnalysisManagerT, ExtraArgTs...>::run(
if (!PI.runBeforePass<IRUnitT>(*Pass, IR))
continue;

StackTraceEntry Entry(PI, *Pass, IR);
PreservedAnalyses PassPA = Pass->run(IR, AM, ExtraArgs...);

// Update the analysis manager as each pass runs and potentially
Expand Down
12 changes: 12 additions & 0 deletions llvm/lib/IR/PassManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,18 @@ PreservedAnalyses ModuleToFunctionPassAdaptor::run(Module &M,
return PA;
}

template <>
void llvm::printIRUnitNameForStackTrace<Module>(raw_ostream &OS,
const Module &IR) {
OS << "module \"" << IR.getName() << "\"";
}

template <>
void llvm::printIRUnitNameForStackTrace<Function>(raw_ostream &OS,
const Function &IR) {
OS << "function \"" << IR.getName() << "\"";
}

AnalysisSetKey CFGAnalyses::SetKey;

AnalysisSetKey PreservedAnalyses::AllAnalysesKey;
16 changes: 13 additions & 3 deletions llvm/lib/Passes/PassBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -325,15 +325,25 @@ AnalysisKey NoOpLoopAnalysis::Key;

namespace {

// A pass for testing -print-on-crash.
// Passes for testing crashes.
// DO NOT USE THIS EXCEPT FOR TESTING!
class TriggerCrashPass : public PassInfoMixin<TriggerCrashPass> {
class TriggerCrashModulePass : public PassInfoMixin<TriggerCrashModulePass> {
public:
PreservedAnalyses run(Module &, ModuleAnalysisManager &) {
abort();
return PreservedAnalyses::all();
}
static StringRef name() { return "TriggerCrashPass"; }
static StringRef name() { return "TriggerCrashModulePass"; }
};

class TriggerCrashFunctionPass
: public PassInfoMixin<TriggerCrashFunctionPass> {
public:
PreservedAnalyses run(Function &, FunctionAnalysisManager &) {
abort();
return PreservedAnalyses::all();
}
static StringRef name() { return "TriggerCrashFunctionPass"; }
};

// A pass for testing message reporting of -verify-each failures.
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/Passes/PassRegistry.def
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ MODULE_PASS("strip-debug-declare", StripDebugDeclarePass())
MODULE_PASS("strip-nondebug", StripNonDebugSymbolsPass())
MODULE_PASS("strip-nonlinetable-debuginfo", StripNonLineTableDebugInfoPass())
MODULE_PASS("synthetic-counts-propagation", SyntheticCountsPropagation())
MODULE_PASS("trigger-crash", TriggerCrashPass())
MODULE_PASS("trigger-crash-module", TriggerCrashModulePass())
MODULE_PASS("trigger-verifier-error", TriggerVerifierErrorPass())
MODULE_PASS("tsan-module", ModuleThreadSanitizerPass())
MODULE_PASS("verify", VerifierPass())
Expand Down Expand Up @@ -459,6 +459,7 @@ FUNCTION_PASS("structurizecfg", StructurizeCFGPass())
FUNCTION_PASS("tailcallelim", TailCallElimPass())
FUNCTION_PASS("tlshoist", TLSVariableHoistPass())
FUNCTION_PASS("transform-warning", WarnMissedTransformationsPass())
FUNCTION_PASS("trigger-crash-function", TriggerCrashFunctionPass())
FUNCTION_PASS("trigger-verifier-error", TriggerVerifierErrorPass())
FUNCTION_PASS("tsan", ThreadSanitizerPass())
FUNCTION_PASS("typepromotion", TypePromotionPass(TM))
Expand Down
20 changes: 20 additions & 0 deletions llvm/test/Other/crash-stack-trace.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
; REQUIRES: asserts

; RUN: not --crash opt -passes=trigger-crash-module %s -disable-output 2>&1 | \
; RUN: FileCheck %s --check-prefix=CHECK-MODULE

; CHECK-MODULE: Stack dump:
; CHECK-MODULE-NEXT: 0. Program arguments:
; CHECK-MODULE-NEXT: 1. Running pass "trigger-crash-module" on module "{{.*}}crash-stack-trace.ll"

; RUN: not --crash opt -passes='sroa,trigger-crash-function' %s -disable-output 2>&1 | \
; RUN: FileCheck %s --check-prefix=CHECK-FUNCTION

; CHECK-FUNCTION: Stack dump:
; CHECK-FUNCTION-NEXT: 0. Program arguments:
; CHECK-FUNCTION-NEXT: 1. Running pass "function(sroa<modify-cfg>,trigger-crash-function)" on module "{{.*}}crash-stack-trace.ll"
; CHECK-FUNCTION-NEXT: 2. Running pass "trigger-crash-function" on function "foo"

define void @foo() {
ret void
}
12 changes: 6 additions & 6 deletions llvm/test/Other/print-on-crash.ll
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
; A test that the hidden option -print-on-crash properly sets a signal handler
; which gets called when a pass crashes. The trigger-crash pass asserts.
; which gets called when a pass crashes. The trigger-crash-module pass asserts.

; RUN: not --crash opt -print-on-crash -passes=trigger-crash < %s 2>&1 | FileCheck %s --check-prefix=CHECK_SIMPLE
; RUN: not --crash opt -print-on-crash -passes=trigger-crash-module < %s 2>&1 | FileCheck %s --check-prefix=CHECK_SIMPLE

; RUN: not --crash opt -print-on-crash-path=%t -passes=trigger-crash < %s
; RUN: not --crash opt -print-on-crash-path=%t -passes=trigger-crash-module < %s
; RUN: FileCheck %s --check-prefix=CHECK_SIMPLE --input-file=%t

; A test that the signal handler set by the hidden option -print-on-crash
; is not called when no pass crashes.

; RUN: opt -disable-output -print-on-crash -passes="default<O2>" < %s 2>&1 | FileCheck %s --check-prefix=CHECK_NO_CRASH --allow-empty

; RUN: not --crash opt -print-on-crash -print-module-scope -passes=trigger-crash < %s 2>&1 | FileCheck %s --check-prefix=CHECK_MODULE
; RUN: not --crash opt -print-on-crash -print-module-scope -passes=trigger-crash-module < %s 2>&1 | FileCheck %s --check-prefix=CHECK_MODULE

; RUN: not --crash opt -print-on-crash -print-module-scope -passes=trigger-crash -filter-passes=trigger-crash < %s 2>&1 | FileCheck %s --check-prefix=CHECK_MODULE
; RUN: not --crash opt -print-on-crash -print-module-scope -passes=trigger-crash-module -filter-passes=trigger-crash-module < %s 2>&1 | FileCheck %s --check-prefix=CHECK_MODULE

; RUN: not --crash opt -print-on-crash -print-module-scope -passes=trigger-crash -filter-passes=blah < %s 2>&1 | FileCheck %s --check-prefix=CHECK_FILTERED
; RUN: not --crash opt -print-on-crash -print-module-scope -passes=trigger-crash-module -filter-passes=blah < %s 2>&1 | FileCheck %s --check-prefix=CHECK_FILTERED

; CHECK_SIMPLE: *** Dump of IR Before Last Pass {{.*}} Started ***
; CHECK_SIMPLE: @main
Expand Down
Loading