--- /dev/null
+//===- FileLineColLocBreakpointManager.h - TODO: add message ----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef MLIR_TRACING_BREAKPOINTMANAGERS_FILELINECOLLOCBREAKPOINTMANAGER_H
+#define MLIR_TRACING_BREAKPOINTMANAGERS_FILELINECOLLOCBREAKPOINTMANAGER_H
+
+#include "mlir/Debug/BreakpointManager.h"
+#include "mlir/Debug/ExecutionContext.h"
+#include "mlir/IR/Action.h"
+#include "mlir/IR/Location.h"
+#include "mlir/IR/Operation.h"
+#include "llvm/ADT/DenseMap.h"
+#include <memory>
+#include <optional>
+
+namespace mlir {
+namespace tracing {
+
+/// This breakpoing intends to match a FileLineColLocation, that is a tuple of
+/// file name, line number, and column number. Using -1 for the column and the
+/// line number will match any column and line number respectively.
+class FileLineColLocBreakpoint
+ : public BreakpointBase<FileLineColLocBreakpoint> {
+public:
+ FileLineColLocBreakpoint(StringRef file, int64_t line, int64_t col)
+ : line(line), col(col) {}
+
+ void print(raw_ostream &os) const override {
+ os << "Location: " << file << ':' << line << ':' << col;
+ }
+
+ /// Parse a string representation in the form of "<file>:<line>:<col>". Return
+ /// a tuple with these three elements, the first one is a StringRef pointing
+ /// into the original string.
+ static FailureOr<std::tuple<StringRef, int64_t, int64_t>> parseFromString(
+ StringRef str, llvm::function_ref<void(Twine)> diag = [](Twine) {});
+
+private:
+ /// A filename on which to break.
+ StringRef file;
+
+ /// A particular line on which to break, or -1 to break on any line.
+ int64_t line;
+
+ /// A particular column on which to break, or -1 to break on any column
+ int64_t col;
+
+ friend class FileLineColLocBreakpointManager;
+};
+
+/// This breakpoint manager is responsible for matching
+/// FileLineColLocBreakpoint. It'll extract the location from the action context
+/// looking for a FileLineColLocation, and match it against the registered
+/// breakpoints.
+class FileLineColLocBreakpointManager
+ : public BreakpointManagerBase<FileLineColLocBreakpointManager> {
+public:
+ Breakpoint *match(const Action &action) const override {
+ for (const IRUnit &unit : action.getContextIRUnits()) {
+ if (auto *op = unit.dyn_cast<Operation *>()) {
+ if (auto match = matchFromLocation(op->getLoc()))
+ return *match;
+ continue;
+ }
+ if (auto *block = unit.dyn_cast<Block *>()) {
+ for (auto &op : block->getOperations()) {
+ if (auto match = matchFromLocation(op.getLoc()))
+ return *match;
+ }
+ continue;
+ }
+ if (Region *region = unit.dyn_cast<Region *>()) {
+ if (auto match = matchFromLocation(region->getLoc()))
+ return *match;
+ continue;
+ }
+ }
+ return {};
+ }
+
+ FileLineColLocBreakpoint *addBreakpoint(StringRef file, int64_t line,
+ int64_t col = -1) {
+ auto &breakpoint = breakpoints[std::make_tuple(file, line, col)];
+ if (!breakpoint)
+ breakpoint = std::make_unique<FileLineColLocBreakpoint>(file, line, col);
+ return breakpoint.get();
+ }
+
+private:
+ std::optional<Breakpoint *> matchFromLocation(Location initialLoc) const {
+ std::optional<Breakpoint *> match = std::nullopt;
+ initialLoc->walk([&](Location loc) {
+ auto fileLoc = loc.dyn_cast<FileLineColLoc>();
+ if (!fileLoc)
+ return WalkResult::advance();
+ StringRef file = fileLoc.getFilename();
+ int64_t line = fileLoc.getLine();
+ int64_t col = fileLoc.getColumn();
+ auto lookup = breakpoints.find(std::make_tuple(file, line, col));
+ if (lookup != breakpoints.end() && lookup->second->isEnabled()) {
+ match = lookup->second.get();
+ return WalkResult::interrupt();
+ }
+ // If not found, check with the -1 key if we have a breakpoint for any
+ // col.
+ lookup = breakpoints.find(std::make_tuple(file, line, -1));
+ if (lookup != breakpoints.end() && lookup->second->isEnabled()) {
+ match = lookup->second.get();
+ return WalkResult::interrupt();
+ }
+ // If not found, check with the -1 key if we have a breakpoint for any
+ // line.
+ lookup = breakpoints.find(std::make_tuple(file, -1, -1));
+ if (lookup != breakpoints.end() && lookup->second->isEnabled()) {
+ match = lookup->second.get();
+ return WalkResult::interrupt();
+ }
+ return WalkResult::advance();
+ });
+ return match;
+ }
+
+ /// A map from a (filename, line, column) -> breakpoint.
+ DenseMap<std::tuple<StringRef, int64_t, int64_t>,
+ std::unique_ptr<FileLineColLocBreakpoint>>
+ breakpoints;
+};
+
+} // namespace tracing
+} // namespace mlir
+
+#endif // MLIR_TRACING_BREAKPOINTMANAGERS_FILELINECOLLOCBREAKPOINTMANAGER_H
bool willExecute) override;
void afterExecute(const ActionActiveStack *action) override;
+ /// If one of multiple breakpoint managers are set, only actions that are
+ /// matching a breakpoint will be logged.
+ void addBreakpointManager(const BreakpointManager *manager) {
+ breakpointManagers.push_back(manager);
+ }
+
private:
+ /// Check if we should log this action or not.
+ bool shouldLog(const ActionActiveStack *action);
+
raw_ostream &os;
bool printActions;
bool printBreakpoints;
bool printIRUnits;
+ std::vector<const BreakpointManager *> breakpointManagers;
};
} // namespace tracing
#ifndef MLIR_TOOLS_MLIROPT_MLIROPTMAIN_H
#define MLIR_TOOLS_MLIROPT_MLIROPTMAIN_H
+#include "mlir/Debug/BreakpointManagers/FileLineColLocBreakpointManager.h"
#include "mlir/Support/LogicalResult.h"
#include "llvm/ADT/StringRef.h"
class DialectRegistry;
class PassPipelineCLParser;
class PassManager;
+namespace tracing {
+class FileLineColLocBreakpointManager;
+}
/// Configuration options for the mlir-opt tool.
/// This is intended to help building tools like mlir-opt by collecting the
/// Get the filename to use for logging actions.
StringRef getLogActionsTo() const { return logActionsToFlag; }
+ /// Set a location breakpoint manager to filter out action logging based on
+ /// the attached IR location in the Action context. Ownership stays with the
+ /// caller.
+ void addLogActionLocFilter(tracing::BreakpointManager *breakpointManager) {
+ logActionLocationFilter.push_back(breakpointManager);
+ }
+
+ /// Get the location breakpoint managers to use to filter out action logging.
+ ArrayRef<tracing::BreakpointManager *> getLogActionsLocFilters() const {
+ return logActionLocationFilter;
+ }
+
/// Set the callback to populate the pass manager.
MlirOptMainConfig &
setPassPipelineSetupFn(std::function<LogicalResult(PassManager &)> callback) {
/// Log action execution to the given file (or "-" for stdout)
std::string logActionsToFlag;
+ /// Location Breakpoints to filter the action logging.
+ std::vector<tracing::BreakpointManager *> logActionLocationFilter;
+
/// The callback to populate the pass manager.
std::function<LogicalResult(PassManager &)> passPipelineCallback;
--- /dev/null
+//===- FileLineColLocBreakpointManager.cpp - MLIR Optimizer Driver --------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Debug/BreakpointManagers/FileLineColLocBreakpointManager.h"
+#include "mlir/IR/Diagnostics.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace mlir;
+using namespace mlir::tracing;
+
+FailureOr<std::tuple<StringRef, int64_t, int64_t>>
+FileLineColLocBreakpoint::parseFromString(StringRef str,
+ function_ref<void(Twine)> diag) {
+ // Watch at debug locations arguments are expected to be in the form:
+ // `fileName:line:col`, `fileName:line`, or `fileName`.
+
+ if (str.empty()) {
+ if (diag)
+ diag("error: initializing FileLineColLocBreakpoint with empty file name");
+ return failure();
+ }
+
+ // This logic is complex because on Windows `:` is a comment valid path
+ // character: `C:\...`.
+ auto [fileLine, colStr] = str.rsplit(':');
+ auto [file, lineStr] = fileLine.rsplit(':');
+ // Extract the line and column value
+ int64_t line = -1, col = -1;
+ if (lineStr.empty()) {
+ // No candidate for line number, try to use the column string as line
+ // instead.
+ file = fileLine;
+ if (!colStr.empty() && colStr.getAsInteger(0, line))
+ file = str;
+ } else {
+ if (lineStr.getAsInteger(0, line)) {
+ // Failed to parse a line number, try to use the column string as line
+ // instead. If this failed as well, the entire string is the file name.
+ file = fileLine;
+ if (colStr.getAsInteger(0, line))
+ file = str;
+ } else {
+ // We successfully parsed a line number, try to parse the column number.
+ // This shouldn't fail, or the entire string is the file name.
+ if (colStr.getAsInteger(0, col)) {
+ file = str;
+ line = -1;
+ }
+ }
+ }
+ return std::tuple<StringRef, int64_t, int64_t>{file, line, col};
+}
add_mlir_library(MLIRDebug
DebugCounter.cpp
ExecutionContext.cpp
+ BreakpointManagers/FileLineColLocBreakpointManager.cpp
ADDITIONAL_HEADER_DIRS
${MLIR_MAIN_INCLUDE_DIR}/mlir/Debug
//===----------------------------------------------------------------------===//
#include "mlir/Debug/Observers/ActionLogging.h"
+#include "mlir/Debug/BreakpointManager.h"
#include "mlir/IR/Action.h"
#include "llvm/Support/Threading.h"
#include "llvm/Support/raw_ostream.h"
// ActionLogger
//===----------------------------------------------------------------------===//
+bool ActionLogger::shouldLog(const ActionActiveStack *action) {
+ // If some condition was set, we ensured it is met before logging.
+ if (breakpointManagers.empty())
+ return true;
+ return llvm::any_of(breakpointManagers,
+ [&](const BreakpointManager *manager) {
+ return manager->match(action->getAction());
+ });
+}
+
void ActionLogger::beforeExecute(const ActionActiveStack *action,
Breakpoint *breakpoint, bool willExecute) {
+ if (!shouldLog(action))
+ return;
SmallVector<char> name;
llvm::get_thread_name(name);
if (name.empty()) {
}
void ActionLogger::afterExecute(const ActionActiveStack *action) {
+ if (!shouldLog(action))
+ return;
SmallVector<char> name;
llvm::get_thread_name(name);
if (name.empty()) {
#include "mlir/Tools/ParseUtilities.h"
#include "mlir/Tools/Plugins/DialectPlugin.h"
#include "mlir/Tools/Plugins/PassPlugin.h"
+#include "llvm/ADT/StringRef.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/FileUtilities.h"
#include "llvm/Support/InitLLVM.h"
" '-' is passed"),
cl::location(logActionsToFlag)};
+ static cl::list<std::string> logActionLocationFilter(
+ "log-mlir-actions-filter",
+ cl::desc(
+ "Comma separated list of locations to filter actions from logging"),
+ cl::CommaSeparated,
+ cl::cb<void, std::string>([&](const std::string &location) {
+ static bool register_once = [&] {
+ addLogActionLocFilter(&locBreakpointManager);
+ return true;
+ }();
+ (void)register_once;
+ static std::vector<std::string> locations;
+ locations.push_back(location);
+ StringRef locStr = locations.back();
+
+ // Parse the individual location filters and set the breakpoints.
+ auto diag = [](Twine msg) { llvm::errs() << msg << "\n"; };
+ auto locBreakpoint =
+ tracing::FileLineColLocBreakpoint::parseFromString(locStr, diag);
+ if (failed(locBreakpoint)) {
+ llvm::errs() << "Invalid location filter: " << locStr << "\n";
+ exit(1);
+ }
+ auto [file, line, col] = *locBreakpoint;
+ locBreakpointManager.addBreakpoint(file, line, col);
+ }));
+
static cl::opt<bool, /*ExternalStorage=*/true> showDialects(
"show-dialects",
cl::desc("Print the list of registered dialects and exit"),
/// Pointer to static dialectPlugins variable in constructor, needed by
/// setDialectPluginsCallback(DialectRegistry&).
cl::list<std::string> *dialectPlugins = nullptr;
+
+ /// The breakpoint manager for the log action location filter.
+ tracing::FileLineColLocBreakpointManager locBreakpointManager;
};
} // namespace
logActionsFile->keep();
raw_fd_ostream &logActionsStream = logActionsFile->os();
actionLogger = std::make_unique<tracing::ActionLogger>(logActionsStream);
+ for (const auto *locationBreakpoint : config.getLogActionsLocFilters())
+ actionLogger->addBreakpointManager(locationBreakpoint);
executionContext.registerObserver(actionLogger.get());
context.registerActionHandler(executionContext);
private:
std::unique_ptr<llvm::ToolOutputFile> logActionsFile;
std::unique_ptr<tracing::ActionLogger> actionLogger;
+ std::vector<std::unique_ptr<tracing::FileLineColLocBreakpoint>>
+ locationBreakpoints;
tracing::ExecutionContext executionContext;
};
--- /dev/null
+// Run the canonicalize on each function, use the --log-mlir-actions-filter= option
+// to filter which action should be logged.
+
+func.func @a() {
+ return
+}
+
+func.func @b() {
+ return
+}
+
+func.func @c() {
+ return
+}
+
+////////////////////////////////////
+/// 1. All actions should be logged.
+
+// RUN: mlir-opt %s --log-actions-to=- -pass-pipeline="builtin.module(func.func(canonicalize))" -o %t --mlir-disable-threading | FileCheck %s
+// Specify the current file as filter, expect to see all actions.
+// RUN: mlir-opt %s --log-mlir-actions-filter=%s --log-actions-to=- -pass-pipeline="builtin.module(func.func(canonicalize))" -o %t --mlir-disable-threading | FileCheck %s
+
+// CHECK: [thread {{.*}}] begins (no breakpoint) Action `pass-execution-action` running `Canonicalizer` on Operation `func.func` (func.func @a() {...}
+// CHECK-NEXT: [thread {{.*}}] completed `pass-execution-action`
+// CHECK-NEXT: [thread {{.*}}] begins (no breakpoint) Action `pass-execution-action` running `Canonicalizer` on Operation `func.func` (func.func @b() {...}
+// CHECK-NEXT: [thread {{.*}}] completed `pass-execution-action`
+// CHECK-NEXT: [thread {{.*}}] begins (no breakpoint) Action `pass-execution-action` running `Canonicalizer` on Operation `func.func` (func.func @c() {...}
+// CHECK-NEXT: [thread {{.*}}] completed `pass-execution-action`
+
+////////////////////////////////////
+/// 2. No match
+
+// Specify a non-existing file as filter, expect to see no actions.
+// RUN: mlir-opt %s --log-mlir-actions-filter=foo.mlir --log-actions-to=- -pass-pipeline="builtin.module(func.func(canonicalize))" -o %t --mlir-disable-threading | FileCheck %s --check-prefix=CHECK-NONE --allow-empty
+// Filter on a non-matching line, expect to see no actions.
+// RUN: mlir-opt %s --log-mlir-actions-filter=%s:1 --log-actions-to=- -pass-pipeline="builtin.module(func.func(canonicalize))" -o %t --mlir-disable-threading | FileCheck %s --check-prefix=CHECK-NONE --allow-empty
+
+// Invalid Filter
+// CHECK-NONE-NOT: Canonicalizer
+
+////////////////////////////////////
+/// 3. Matching filters
+
+// Filter the second function only
+// RUN: mlir-opt %s --log-mlir-actions-filter=%s:8 --log-actions-to=- -pass-pipeline="builtin.module(func.func(canonicalize))" -o %t --mlir-disable-threading | FileCheck %s --check-prefix=CHECK-SECOND
+
+// CHECK-SECOND-NOT: @a
+// CHECK-SECOND-NOT: @c
+// CHECK-SECOND: [thread {{.*}}] begins (no breakpoint) Action `pass-execution-action` running `Canonicalizer` on Operation `func.func` (func.func @b() {...}
+// CHECK-SECOND-NEXT: [thread {{.*}}] completed `pass-execution-action`
+
+// Filter the first and third functions
+// RUN: mlir-opt %s --log-mlir-actions-filter=%s:4,%s:12 --log-actions-to=- -pass-pipeline="builtin.module(func.func(canonicalize))" -o %t --mlir-disable-threading | FileCheck %s --check-prefix=CHECK-FIRST-THIRD
+
+// CHECK-FIRST-THIRD-NOT: Canonicalizer
+// CHECK-FIRST-THIRD: [thread {{.*}}] begins (no breakpoint) Action `pass-execution-action` running `Canonicalizer` on Operation `func.func` (func.func @a() {...}
+// CHECK-FIRST-THIRD-NEXT: [thread {{.*}}] completed `pass-execution-action`
+// CHECK-FIRST-THIRD-NEXT: [thread {{.*}}] begins (no breakpoint) Action `pass-execution-action` running `Canonicalizer` on Operation `func.func` (func.func @c() {...}
+// CHECK-FIRST-THIRD-NEXT: [thread {{.*}}] completed `pass-execution-action`
+// CHECK-FIRST-THIRD-NOT: Canonicalizer
add_mlir_unittest(MLIRDebugTests
DebugCounterTest.cpp
ExecutionContextTest.cpp
+ FileLineColLocBreakpointManagerTest.cpp
)
target_link_libraries(MLIRDebugTests
--- /dev/null
+//===- FileLineColLocBreakpointManagerTest.cpp - --------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "mlir/Debug/BreakpointManagers/FileLineColLocBreakpointManager.h"
+#include "mlir/Debug/ExecutionContext.h"
+#include "mlir/IR/Builders.h"
+#include "mlir/IR/BuiltinAttributes.h"
+#include "mlir/IR/BuiltinTypes.h"
+#include "mlir/IR/Location.h"
+#include "mlir/IR/OperationSupport.h"
+#include "llvm/ADT/STLExtras.h"
+#include "gtest/gtest.h"
+
+using namespace mlir;
+using namespace mlir::tracing;
+
+static Operation *createOp(MLIRContext *context, Location loc,
+ StringRef operationName,
+ unsigned int numRegions = 0) {
+ context->allowUnregisteredDialects();
+ return Operation::create(loc, OperationName(operationName, context),
+ std::nullopt, std::nullopt, std::nullopt,
+ std::nullopt, numRegions);
+}
+
+namespace {
+struct FileLineColLocTestingAction
+ : public ActionImpl<FileLineColLocTestingAction> {
+ MLIR_DEFINE_EXPLICIT_INTERNAL_INLINE_TYPE_ID(FileLineColLocTestingAction)
+ static constexpr StringLiteral tag = "file-line-col-loc-testing-action";
+ FileLineColLocTestingAction(ArrayRef<IRUnit> irUnits)
+ : ActionImpl<FileLineColLocTestingAction>(irUnits) {}
+};
+
+TEST(FileLineColLocBreakpointManager, OperationMatch) {
+ // This test will process a sequence of operation and check various situation
+ // with a breakpoint hitting or not based on the location attached to the
+ // operation. When a breakpoint hits, the action is skipped and the counter is
+ // not incremented.
+ ExecutionContext executionCtx(
+ [](const ActionActiveStack *) { return ExecutionContext::Skip; });
+ int counter = 0;
+ auto counterInc = [&]() { counter++; };
+
+ // Setup
+
+ MLIRContext context;
+ // Miscellaneous information to define operations
+ std::vector<StringRef> fileNames = {
+ StringRef("foo.bar"), StringRef("baz.qux"), StringRef("quux.corge")};
+ std::vector<std::pair<unsigned, unsigned>> lineColLoc = {{42, 7}, {24, 3}};
+ Location callee = UnknownLoc::get(&context),
+ caller = UnknownLoc::get(&context), loc = UnknownLoc::get(&context);
+
+ // Set of operations over where we are going to be testing the functionality
+ std::vector<Operation *> operations = {
+ createOp(&context, CallSiteLoc::get(callee, caller),
+ "callSiteLocOperation"),
+ createOp(&context,
+ FileLineColLoc::get(&context, fileNames[0], lineColLoc[0].first,
+ lineColLoc[0].second),
+ "fileLineColLocOperation"),
+ createOp(&context, FusedLoc::get(&context, {}, Attribute()),
+ "fusedLocOperation"),
+ createOp(&context, NameLoc::get(StringAttr::get(&context, fileNames[2])),
+ "nameLocOperation"),
+ createOp(&context, OpaqueLoc::get<void *>(nullptr, loc),
+ "opaqueLocOperation"),
+ createOp(&context,
+ FileLineColLoc::get(&context, fileNames[1], lineColLoc[1].first,
+ lineColLoc[1].second),
+ "anotherFileLineColLocOperation"),
+ createOp(&context, UnknownLoc::get(&context), "unknownLocOperation"),
+ };
+
+ FileLineColLocBreakpointManager breakpointManager;
+ executionCtx.addBreakpointManager(&breakpointManager);
+
+ // Test
+
+ // Basic case is that no breakpoint is set and the counter is incremented for
+ // every op.
+ auto checkNoMatch = [&]() {
+ counter = 0;
+ for (auto enumeratedOp : llvm::enumerate(operations)) {
+ executionCtx(counterInc,
+ FileLineColLocTestingAction({enumeratedOp.value()}));
+ EXPECT_EQ(counter, static_cast<int>(enumeratedOp.index() + 1));
+ }
+ };
+ checkNoMatch();
+
+ // Set a breakpoint matching only the second operation in the list.
+ auto *breakpoint = breakpointManager.addBreakpoint(
+ fileNames[0], lineColLoc[0].first, lineColLoc[0].second);
+ auto checkMatchIdxs = [&](DenseSet<int> idxs) {
+ counter = 0;
+ int reference = 0;
+ for (int i = 0; i < (int)operations.size(); ++i) {
+ executionCtx(counterInc, FileLineColLocTestingAction({operations[i]}));
+ if (!idxs.contains(i))
+ reference++;
+ EXPECT_EQ(counter, reference);
+ }
+ };
+ checkMatchIdxs({1});
+
+ // Check that disabling the breakpoing brings us back to the original
+ // behavior.
+ breakpoint->disable();
+ checkNoMatch();
+
+ // Adding a breakpoint that won't match any location shouldn't affect the
+ // behavior.
+ breakpointManager.addBreakpoint(StringRef("random.file"), 3, 14);
+ checkNoMatch();
+
+ // Set a breakpoint matching only the fifth operation in the list.
+ breakpointManager.addBreakpoint(fileNames[1], lineColLoc[1].first,
+ lineColLoc[1].second);
+ counter = 0;
+ checkMatchIdxs({5});
+
+ // Re-enable the breakpoint matching only the second operation in the list.
+ // We now expect matching of operations 1 and 5.
+ breakpoint->enable();
+ checkMatchIdxs({1, 5});
+
+ for (auto *op : operations) {
+ op->destroy();
+ }
+}
+
+TEST(FileLineColLocBreakpointManager, BlockMatch) {
+ // This test will process a block and check various situation with
+ // a breakpoint hitting or not based on the location attached.
+ // When a breakpoint hits, the action is skipped and the counter is not
+ // incremented.
+ ExecutionContext executionCtx(
+ [](const ActionActiveStack *) { return ExecutionContext::Skip; });
+ int counter = 0;
+ auto counterInc = [&]() { counter++; };
+
+ // Setup
+
+ MLIRContext context;
+ std::vector<StringRef> fileNames = {StringRef("grault.garply"),
+ StringRef("waldo.fred")};
+ std::vector<std::pair<unsigned, unsigned>> lineColLoc = {{42, 7}, {24, 3}};
+ Operation *frontOp = createOp(&context,
+ FileLineColLoc::get(&context, fileNames.front(),
+ lineColLoc.front().first,
+ lineColLoc.front().second),
+ "firstOperation");
+ Operation *backOp = createOp(&context,
+ FileLineColLoc::get(&context, fileNames.back(),
+ lineColLoc.back().first,
+ lineColLoc.back().second),
+ "secondOperation");
+ Block block;
+ block.push_back(frontOp);
+ block.push_back(backOp);
+
+ FileLineColLocBreakpointManager breakpointManager;
+ executionCtx.addBreakpointManager(&breakpointManager);
+
+ // Test
+
+ executionCtx(counterInc, FileLineColLocTestingAction({&block}));
+ EXPECT_EQ(counter, 1);
+
+ auto *breakpoint = breakpointManager.addBreakpoint(
+ fileNames.front(), lineColLoc.front().first, lineColLoc.front().second);
+ counter = 0;
+ executionCtx(counterInc, FileLineColLocTestingAction({&block}));
+ EXPECT_EQ(counter, 0);
+ breakpoint->disable();
+ executionCtx(counterInc, FileLineColLocTestingAction({&block}));
+ EXPECT_EQ(counter, 1);
+
+ breakpoint = breakpointManager.addBreakpoint(
+ fileNames.back(), lineColLoc.back().first, lineColLoc.back().second);
+ counter = 0;
+ executionCtx(counterInc, FileLineColLocTestingAction({&block}));
+ EXPECT_EQ(counter, 0);
+ breakpoint->disable();
+ executionCtx(counterInc, FileLineColLocTestingAction({&block}));
+ EXPECT_EQ(counter, 1);
+}
+
+TEST(FileLineColLocBreakpointManager, RegionMatch) {
+ // This test will process a region and check various situation with
+ // a breakpoint hitting or not based on the location attached.
+ // When a breakpoint hits, the action is skipped and the counter is not
+ // incremented.
+ ExecutionContext executionCtx(
+ [](const ActionActiveStack *) { return ExecutionContext::Skip; });
+ int counter = 0;
+ auto counterInc = [&]() { counter++; };
+
+ // Setup
+
+ MLIRContext context;
+ StringRef fileName("plugh.xyzzy");
+ unsigned line = 42, col = 7;
+ Operation *containerOp =
+ createOp(&context, FileLineColLoc::get(&context, fileName, line, col),
+ "containerOperation", 1);
+ Region ®ion = containerOp->getRegion(0);
+
+ FileLineColLocBreakpointManager breakpointManager;
+ executionCtx.addBreakpointManager(&breakpointManager);
+
+ // Test
+ counter = 0;
+ executionCtx(counterInc, FileLineColLocTestingAction({®ion}));
+ EXPECT_EQ(counter, 1);
+ auto *breakpoint = breakpointManager.addBreakpoint(fileName, line, col);
+ executionCtx(counterInc, FileLineColLocTestingAction({®ion}));
+ EXPECT_EQ(counter, 1);
+ breakpoint->disable();
+ executionCtx(counterInc, FileLineColLocTestingAction({®ion}));
+ EXPECT_EQ(counter, 2);
+
+ containerOp->destroy();
+}
+} // namespace