[PATCH] added enter-leave pass to instrument entering and leaving functions

Bob Haarman via llvm-commits llvm-commits at lists.llvm.org
Wed Nov 4 12:05:06 PST 2015


Allows instrumentation of functions by specifying functions to be called 
when the instrumented function is entered or returned from. The desired 
instrumentation is specified by a map file which names all functions to 
be instrumented. For each such function, it specifies the function to be 
called on entry, the function to be called before returning, or both. 
The called functions receive the name of the instrumented function as 
their first parameter.

CC'ing dnovillo as my best guess as to who may be a good reviewer for 
this code.
-------------- next part --------------
From d0b6bc44384b3af287a0eba3eace491afa5b18d8 Mon Sep 17 00:00:00 2001
From: Bob Haarman <robbert at fb.com>
Date: Mon, 19 Oct 2015 11:13:10 -0700
Subject: [PATCH] added enter-leave pass to instrument entering and leaving
 functions

Allows instrumentation of functions by specifying functions to be called when the instrumented function is entered or returned from. The desired instrumentation is specified by a map file which names all functions to be instrumented. For each such function, it specifies the function to be called on entry, the function to be called before returning, or both. The called functions receive the name of the instrumented function as their first parameter.
---
 include/llvm/InitializePasses.h                    |   1 +
 include/llvm/Transforms/EnterLeave.h               |  60 ++++++++++
 include/llvm/Transforms/EnterLeaveMapParser.h      |  50 ++++++++
 lib/Passes/PassBuilder.cpp                         |   1 +
 lib/Passes/PassRegistry.def                        |   2 +
 lib/Transforms/Instrumentation/CMakeLists.txt      |   3 +
 lib/Transforms/Instrumentation/EnterLeave.cpp      |  88 ++++++++++++++
 .../Instrumentation/EnterLeaveMapParser.cpp        | 129 +++++++++++++++++++++
 lib/Transforms/Instrumentation/Instrumentation.cpp |   1 +
 .../Instrumentation/LegacyEnterLeave.cpp           |  43 +++++++
 test/Instrumentation/EnterLeave/EnterLeave.ll      |  20 ++++
 test/Instrumentation/EnterLeave/EnterLeave.map     |   8 ++
 test/Instrumentation/EnterLeave/errors.ll          |  28 +++++
 test/Instrumentation/EnterLeave/errors.map         |   6 +
 tools/opt/opt.cpp                                  |  18 +++
 15 files changed, 458 insertions(+)
 create mode 100644 include/llvm/Transforms/EnterLeave.h
 create mode 100644 include/llvm/Transforms/EnterLeaveMapParser.h
 create mode 100644 lib/Transforms/Instrumentation/EnterLeave.cpp
 create mode 100644 lib/Transforms/Instrumentation/EnterLeaveMapParser.cpp
 create mode 100644 lib/Transforms/Instrumentation/LegacyEnterLeave.cpp
 create mode 100644 test/Instrumentation/EnterLeave/EnterLeave.ll
 create mode 100644 test/Instrumentation/EnterLeave/EnterLeave.map
 create mode 100644 test/Instrumentation/EnterLeave/errors.ll
 create mode 100644 test/Instrumentation/EnterLeave/errors.map

diff --git a/include/llvm/InitializePasses.h b/include/llvm/InitializePasses.h
index cdd2ba2..d0195df 100644
--- a/include/llvm/InitializePasses.h
+++ b/include/llvm/InitializePasses.h
@@ -149,6 +149,7 @@ void initializeInternalizePassPass(PassRegistry&);
 void initializeIntervalPartitionPass(PassRegistry&);
 void initializeJumpThreadingPass(PassRegistry&);
 void initializeLCSSAPass(PassRegistry&);
+void initializeLegacyEnterLeavePass(PassRegistry&);
 void initializeLICMPass(PassRegistry&);
 void initializeLazyValueInfoPass(PassRegistry&);
 void initializeLintPass(PassRegistry&);
diff --git a/include/llvm/Transforms/EnterLeave.h b/include/llvm/Transforms/EnterLeave.h
new file mode 100644
index 0000000..2a3bcc2
--- /dev/null
+++ b/include/llvm/Transforms/EnterLeave.h
@@ -0,0 +1,60 @@
+//===- EnterLeave.h - Enter/leave instrumentation  --------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// EnterLeave is an LLVM pass which can instrument functions so that they
+/// call another function when control enters or leaves the function being
+/// instrumented. The functions being called will receive the name of the
+/// instrumented function as an ASCIIZ string.
+
+#ifndef LLVM_TRANSFORMS_ENTERLEAVE_H
+#define LLVM_TRANSFORMS_ENTERLEAVE_H
+
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/PassManager.h"
+
+#include <string>
+#include <unordered_map>
+
+using namespace llvm;
+
+namespace llvm {
+
+/// \brief A pass that adds function calls to function entry and returns.
+class EnterLeave {
+public:
+  typedef std::string FunctionName;
+  struct Descriptor {
+    Descriptor() : Enter(NoneType::None), Leave(NoneType::None) {}
+    Optional<FunctionName> Enter;
+    Optional<FunctionName> Leave;
+  };
+  typedef std::unordered_map<FunctionName, Descriptor> Mapping;
+
+  EnterLeave() {}
+
+  static StringRef name() { return "EnterLeave"; }
+
+  /// \brief Instruments function entries and returns in a module.
+  PreservedAnalyses run(Module &M);
+
+  /// \brief Sets the mapping to be used for the pass.
+  static void setMapping(const Mapping &M);
+
+private:
+  /// \brief Instruments the entries and returns of a single function.
+  PreservedAnalyses run(Function &F, Module &M, Type *VoidTy, Type *Int8PtrTy);
+};
+
+} // namespace llvm
+
+#endif
diff --git a/include/llvm/Transforms/EnterLeaveMapParser.h b/include/llvm/Transforms/EnterLeaveMapParser.h
new file mode 100644
index 0000000..d8fccac
--- /dev/null
+++ b/include/llvm/Transforms/EnterLeaveMapParser.h
@@ -0,0 +1,50 @@
+//===- EnterLeaveMapParser.h - Parser for enter/leave maps ------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file contains the interface of a parser for map files to
+/// configure the enter/leave pass.
+
+#ifndef LLVM_TRANSFORMS_ENTERLEAVEMAPPARSER_H
+#define LLVM_TRANSFORMS_ENTERLEAVEMAPPARSER_H
+
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Transforms/EnterLeave.h"
+
+#include <vector>
+
+namespace llvm {
+namespace EnterLeaveMapParser {
+
+/// \brief Parse result.
+/// If the parse was successful, isSuccessfulParse will return true and
+/// getMapping will return a non-null pointer. Conversely, if the parse
+/// was unsuccessful, getMapping will return a null pointer,
+/// isSuccessfulParse will return false, and getDiagnostics will return
+/// a vector containing one or more diagnostics.
+class ParseResult {
+public:
+  const std::vector<SMDiagnostic> Diagnostics;
+  std::unique_ptr<const EnterLeave::Mapping> Mapping;
+
+  ParseResult(const std::vector<SMDiagnostic> &Diagnostics)
+      : Diagnostics(Diagnostics), Mapping(nullptr) {}
+
+  ParseResult(std::unique_ptr<const EnterLeave::Mapping> Mapping)
+      : Diagnostics(std::vector<SMDiagnostic>()), Mapping(std::move(Mapping)) {}
+
+  bool isSuccessfulParse() const { return Diagnostics.empty(); }
+};
+
+ParseResult parseEnterLeaveMapFiles(const std::vector<std::string> &FileNames);
+
+} // namespace EnterLeaveMapParser
+} // namespace llvm
+
+#endif
diff --git a/lib/Passes/PassBuilder.cpp b/lib/Passes/PassBuilder.cpp
index b24c615..1b299db 100644
--- a/lib/Passes/PassBuilder.cpp
+++ b/lib/Passes/PassBuilder.cpp
@@ -29,6 +29,7 @@
 #include "llvm/IR/Verifier.h"
 #include "llvm/Support/Debug.h"
 #include "llvm/Target/TargetMachine.h"
+#include "llvm/Transforms/EnterLeave.h"
 #include "llvm/Transforms/InstCombine/InstCombine.h"
 #include "llvm/Transforms/IPO/StripDeadPrototypes.h"
 #include "llvm/Transforms/Scalar/ADCE.h"
diff --git a/lib/Passes/PassRegistry.def b/lib/Passes/PassRegistry.def
index 638f606..bf0468f 100644
--- a/lib/Passes/PassRegistry.def
+++ b/lib/Passes/PassRegistry.def
@@ -27,6 +27,8 @@ MODULE_ANALYSIS("targetlibinfo", TargetLibraryAnalysis())
 #ifndef MODULE_PASS
 #define MODULE_PASS(NAME, CREATE_PASS)
 #endif
+
+MODULE_PASS("enter-leave", EnterLeave())
 MODULE_PASS("invalidate<all>", InvalidateAllAnalysesPass())
 MODULE_PASS("no-op-module", NoOpModulePass())
 MODULE_PASS("print", PrintModulePass(dbgs()))
diff --git a/lib/Transforms/Instrumentation/CMakeLists.txt b/lib/Transforms/Instrumentation/CMakeLists.txt
index 9b81f4b..21e357a 100644
--- a/lib/Transforms/Instrumentation/CMakeLists.txt
+++ b/lib/Transforms/Instrumentation/CMakeLists.txt
@@ -2,10 +2,13 @@ add_llvm_library(LLVMInstrumentation
   AddressSanitizer.cpp
   BoundsChecking.cpp
   DataFlowSanitizer.cpp
+  EnterLeave.cpp
+  EnterLeaveMapParser.cpp
   GCOVProfiling.cpp
   MemorySanitizer.cpp
   Instrumentation.cpp
   InstrProfiling.cpp
+  LegacyEnterLeave.cpp
   SafeStack.cpp
   SanitizerCoverage.cpp
   ThreadSanitizer.cpp
diff --git a/lib/Transforms/Instrumentation/EnterLeave.cpp b/lib/Transforms/Instrumentation/EnterLeave.cpp
new file mode 100644
index 0000000..59d38cb
--- /dev/null
+++ b/lib/Transforms/Instrumentation/EnterLeave.cpp
@@ -0,0 +1,88 @@
+//===- EnterLeave.cpp - Enter/leave instrumentation  ------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// EnterLeave is an LLVM pass which can instrument functions so that they
+/// call another function when control enters or leaves the function being
+/// instrumented. The functions being called will receive the name of the
+/// instrumented function as an ASCIIZ string.
+
+#define DEBUG_TYPE "enter-leave"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/BasicBlock.h"
+#include "llvm/IR/Constant.h"
+#include "llvm/IR/Function.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/Module.h"
+#include "llvm/IR/IRBuilder.h"
+#include "llvm/IR/PassManager.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Transforms/EnterLeave.h"
+
+#include <string>
+
+using namespace llvm;
+
+namespace {
+EnterLeave::Mapping Mappings;
+}
+
+namespace llvm {
+PreservedAnalyses EnterLeave::run(Module &M) {
+  auto PA = PreservedAnalyses::all();
+  auto &C = M.getContext();
+  auto *VoidTy = Type::getVoidTy(C);
+  auto *Int8PtrTy = PointerType::get(IntegerType::getInt8Ty(C), 0);
+  for (auto &F : M.functions())
+    PA.intersect(run(F, M, VoidTy, Int8PtrTy));
+  return PA;
+}
+
+PreservedAnalyses EnterLeave::run(Function &F, Module &M, Type *VoidTy,
+                                  Type *Int8PtrTy) {
+
+  auto PA = PreservedAnalyses::all();
+  auto FunctionName = F.getName();
+  auto MappingIter = Mappings.find(FunctionName);
+  if (MappingIter == Mappings.end())
+    return PA;
+  auto Description = MappingIter->second;
+
+  IRBuilder<> IRB(F.getEntryBlock().getFirstInsertionPt());
+  auto *FunctionNamePtr = IRB.CreateGlobalStringPtr(FunctionName);
+
+  if (Description.Enter.hasValue()) {
+    DEBUG(dbgs() << "[EnterLeave] adding call to " << *Description.Enter
+                 << " on entry to " << FunctionName << "\n");
+    auto *EnterFunction =
+        M.getOrInsertFunction(*Description.Enter, VoidTy, Int8PtrTy, nullptr);
+    IRB.CreateCall(EnterFunction, FunctionNamePtr);
+    PA = PreservedAnalyses::none();
+  }
+
+  if (Description.Leave.hasValue()) {
+    DEBUG(dbgs() << "[EnterLeave] adding calls to " << *Description.Leave
+                 << " on returns from " << FunctionName << "\n");
+    auto *LeaveFunction =
+        M.getOrInsertFunction(*Description.Leave, VoidTy, Int8PtrTy, nullptr);
+    for (auto &BB : F) {
+      if (auto *RI = dyn_cast_or_null<ReturnInst>(BB.getTerminator())) {
+        IRB.SetInsertPoint(RI);
+        IRB.CreateCall(LeaveFunction, FunctionNamePtr);
+        PA = PreservedAnalyses::none();
+      }
+    }
+  }
+
+  return PA;
+}
+
+void EnterLeave::setMapping(const Mapping &M) { Mappings = M; }
+
+} // namespace llvm
diff --git a/lib/Transforms/Instrumentation/EnterLeaveMapParser.cpp b/lib/Transforms/Instrumentation/EnterLeaveMapParser.cpp
new file mode 100644
index 0000000..f272200
--- /dev/null
+++ b/lib/Transforms/Instrumentation/EnterLeaveMapParser.cpp
@@ -0,0 +1,129 @@
+//===- EnterLeaveMapParser.cpp - Parser for enter/leave maps ----*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This file contains an implementation of a parser for map files to
+/// configure the enter/leave pass.
+
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/YAMLParser.h"
+#include "llvm/Transforms/EnterLeave.h"
+#include "llvm/Transforms/EnterLeaveMapParser.h"
+
+#include <vector>
+
+namespace llvm {
+namespace EnterLeaveMapParser {
+static void processMapping(yaml::MappingNode &M, yaml::Stream &Stream,
+                           SourceMgr &SM, EnterLeave::Mapping &Mapping,
+                           std::vector<SMDiagnostic> &Diagnostics) {
+  // We try to gather as many diagnostics as we can in one go. This is why
+  // we continue processing after the detection of some errors. For example,
+  // if one entry is found to be invalid, we still look at the entries after
+  // it. Only when further processing cannot be done do we use continue to
+  // skip to the next iteration of the loop.
+  for (auto &Entry : M) {
+    auto *Func = dyn_cast<yaml::ScalarNode>(Entry.getKey());
+    if (!Func)
+      Diagnostics.push_back(SM.GetMessage(Entry.getSourceRange().Start,
+                                          SourceMgr::DiagKind::DK_Error,
+                                          "must be a function name."));
+
+    auto *Desc = dyn_cast<yaml::MappingNode>(Entry.getValue());
+    if (!Desc) {
+      Diagnostics.push_back(
+          SM.GetMessage(Entry.getValue()->getSourceRange().Start,
+                        SourceMgr::DiagKind::DK_Error,
+                        "must be a mapping containing enter and/or leave."));
+      continue;
+    }
+
+    EnterLeave::Descriptor Descriptor;
+    for (auto &It : *Desc) {
+      auto *Type = dyn_cast<yaml::ScalarNode>(It.getKey());
+      if (!Type)
+        Diagnostics.push_back(SM.GetMessage(It.getKey()->getSourceRange().Start,
+                                            SourceMgr::DiagKind::DK_Error,
+                                            "must be 'enter' or 'leave'."));
+      SmallString<32> TypeStorage;
+      auto TypeName = Type->getValue(TypeStorage);
+
+      auto *TargetFunc = dyn_cast<yaml::ScalarNode>(It.getValue());
+      if (!TargetFunc) {
+        Diagnostics.push_back(SM.GetMessage(
+            It.getValue()->getSourceRange().Start,
+            SourceMgr::DiagKind::DK_Error, "must be a function name."));
+        continue;
+      }
+      SmallString<32> TargetFuncStorage;
+      auto TargetFuncName = TargetFunc->getValue(TargetFuncStorage);
+
+      if (TypeName == "enter")
+        Descriptor.Enter = TargetFuncName;
+      else if (TypeName == "leave")
+        Descriptor.Leave = TargetFuncName;
+      else {
+        Diagnostics.push_back(SM.GetMessage(
+            Type->getSourceRange().Start, SourceMgr::DiagKind::DK_Error,
+            "unsupported mapping type: " + TypeName.str()));
+        continue;
+      }
+    }
+
+    SmallString<32> FuncStorage;
+    Mapping.emplace(Func->getValue(FuncStorage), Descriptor);
+  }
+}
+
+static void parseBuffer(MemoryBufferRef Buffer, EnterLeave::Mapping &Mapping,
+                        std::vector<SMDiagnostic> &Diagnostics) {
+  SourceMgr SM;
+  yaml::Stream Stream(Buffer, SM);
+  for (auto &Document : Stream) {
+    auto *Node = Document.getRoot();
+    if (Node && !isa<yaml::NullNode>(Node)) {
+      if (auto *M = dyn_cast<yaml::MappingNode>(Node))
+        processMapping(*M, Stream, SM, Mapping, Diagnostics);
+      else
+        Diagnostics.push_back(SM.GetMessage(Node->getSourceRange().Start,
+                                            SourceMgr::DiagKind::DK_Error,
+                                            "must be a mapping"));
+    }
+  }
+}
+
+static void parseEnterLeaveMapFile(const std::string &FileName,
+                                   EnterLeave::Mapping &Mapping,
+                                   std::vector<SMDiagnostic> &Diagnostics) {
+  auto Buffer = MemoryBuffer::getFile(FileName);
+  if (Buffer)
+    parseBuffer((*Buffer)->getMemBufferRef(), Mapping, Diagnostics);
+  else {
+    auto E = SMDiagnostic(FileName, SourceMgr::DiagKind::DK_Error,
+                          Buffer.getError().message());
+    Diagnostics.push_back(E);
+    return;
+  }
+}
+
+ParseResult parseEnterLeaveMapFiles(const std::vector<std::string> &FileNames) {
+  auto *Mapping = new EnterLeave::Mapping();
+  std::vector<SMDiagnostic> Diagnostics;
+  for (const auto &FileName : FileNames)
+    parseEnterLeaveMapFile(FileName, *Mapping, Diagnostics);
+  if (Diagnostics.empty())
+    return ParseResult(std::unique_ptr<EnterLeave::Mapping>(Mapping));
+  else
+    return ParseResult(Diagnostics);
+}
+} // namespace EnterLeaveMapParser
+} // namespace llvm
diff --git a/lib/Transforms/Instrumentation/Instrumentation.cpp b/lib/Transforms/Instrumentation/Instrumentation.cpp
index c504b5a..cd93da6 100644
--- a/lib/Transforms/Instrumentation/Instrumentation.cpp
+++ b/lib/Transforms/Instrumentation/Instrumentation.cpp
@@ -61,6 +61,7 @@ void llvm::initializeInstrumentation(PassRegistry &Registry) {
   initializeBoundsCheckingPass(Registry);
   initializeGCOVProfilerPass(Registry);
   initializeInstrProfilingPass(Registry);
+  initializeLegacyEnterLeavePass(Registry);
   initializeMemorySanitizerPass(Registry);
   initializeThreadSanitizerPass(Registry);
   initializeSanitizerCoverageModulePass(Registry);
diff --git a/lib/Transforms/Instrumentation/LegacyEnterLeave.cpp b/lib/Transforms/Instrumentation/LegacyEnterLeave.cpp
new file mode 100644
index 0000000..a0504ca
--- /dev/null
+++ b/lib/Transforms/Instrumentation/LegacyEnterLeave.cpp
@@ -0,0 +1,43 @@
+//===- LegacyEnterLeave.cpp - EnterLeave (legacy pass manager)  -*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+/// \file
+/// This wraps EnterLeave so that it can be used with the legacy pass
+/// manager.
+
+#define DEBUG_TYPE "enter-leave"
+#include "llvm/Pass.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/IR/Module.h"
+#include "llvm/Transforms/EnterLeave.h"
+
+using namespace llvm;
+
+namespace {
+
+/// \brief Implementation of EnterLeave for the legacy pass manager.
+class LegacyEnterLeave : public ModulePass {
+public:
+  static char ID; // Pass identification, replacement for typeid.
+
+  LegacyEnterLeave() : ModulePass(ID) {
+    initializeLegacyEnterLeavePass(*PassRegistry::getPassRegistry());
+  }
+
+  virtual bool runOnModule(Module &M) { return !Impl.run(M).areAllPreserved(); }
+
+private:
+  EnterLeave Impl;
+};
+
+char LegacyEnterLeave::ID = 0;
+
+} // anonymous namespace
+
+INITIALIZE_PASS(LegacyEnterLeave, "enter-leave", "Enter/leave instrumentation",
+                false, false)
diff --git a/test/Instrumentation/EnterLeave/EnterLeave.ll b/test/Instrumentation/EnterLeave/EnterLeave.ll
new file mode 100644
index 0000000..14f6033
--- /dev/null
+++ b/test/Instrumentation/EnterLeave/EnterLeave.ll
@@ -0,0 +1,20 @@
+; RUN: opt -passes=enter-leave -enter-leave-map %p/EnterLeave.map %s -o - \
+; RUN:  | llvm-dis | FileCheck %s
+
+; CHECK: @0 = private unnamed_addr constant [5 x i8] c"quus\00"
+; CHECK: @1 = private unnamed_addr constant [5 x i8] c"main\00"
+
+define i32 @quus(i32 %x, i32 %y) {
+; CHECK: call void @_enter(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @0, i32 0, i32 0))
+  %1 = add nsw i32 %y, %x
+; CHECK: call void @_leave(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @0, i32 0, i32 0))
+  ret i32 %1
+}
+
+define i32 @main(i32 %argc, i8** nocapture readnone %argv) {
+; CHECK-NOT: call
+; CHECK: call i32 @quus(i32 1, i32 3)
+  %1 = call i32 @quus(i32 1, i32 3)
+; CHECK: call void @_leave(i8* getelementptr inbounds ([5 x i8], [5 x i8]* @1, i32 0, i32 0))
+  ret i32 %1
+}
diff --git a/test/Instrumentation/EnterLeave/EnterLeave.map b/test/Instrumentation/EnterLeave/EnterLeave.map
new file mode 100644
index 0000000..d13274b
--- /dev/null
+++ b/test/Instrumentation/EnterLeave/EnterLeave.map
@@ -0,0 +1,8 @@
+quus: {
+  enter: _enter,
+  leave: _leave,
+}
+
+main: {
+  leave: _leave,
+}
diff --git a/test/Instrumentation/EnterLeave/errors.ll b/test/Instrumentation/EnterLeave/errors.ll
new file mode 100644
index 0000000..f13fff1
--- /dev/null
+++ b/test/Instrumentation/EnterLeave/errors.ll
@@ -0,0 +1,28 @@
+; RUN: not opt -passes=enter-leave -enter-leave-map %p/errors.map %s -o - \
+; RUN:  2>&1 | FileCheck %s
+
+; CHECK: Error parsing enter/leave map files.
+; CHECK: {{.*}}/opt:
+; CHECK-SAME: {{.*}}/errors.map:2:3: error:
+; CHECK-SAME: unsupported mapping type: invalid-mapping
+; CHECK: invalid-mapping
+; CHECK: {{.*}}/opt:
+; CHECK-SAME: {{.*}}/errors.map:4:6: error:
+; CHECK-SAME: must be a mapping containing enter and/or leave.
+; CHECK: qux
+; CHECK: {{.*}}/errors.map:5:8: error:
+; CHECK-SAME: must be a mapping containing enter and/or leave.
+; CHECK: [ enter, spam, leave ]
+; CHECK: {{.*}}/opt:
+; CHECK-SAME: {{.*}}/errors.map:7:1: error:
+; CHECK-SAME: must be a mapping containing enter and/or leave.
+
+define i32 @quus(i32 %x, i32 %y) {
+  %1 = add nsw i32 %y, %x
+  ret i32 %1
+}
+
+define i32 @main(i32 %argc, i8** nocapture readnone %argv) {
+  %1 = call i32 @quus(i32 1, i32 3)
+  ret i32 %1
+}
diff --git a/test/Instrumentation/EnterLeave/errors.map b/test/Instrumentation/EnterLeave/errors.map
new file mode 100644
index 0000000..fde9351
--- /dev/null
+++ b/test/Instrumentation/EnterLeave/errors.map
@@ -0,0 +1,6 @@
+foo: {
+  invalid-mapping: bar,
+}
+baz: qux
+quz: [ enter, spam, leave ]
+quuz
diff --git a/tools/opt/opt.cpp b/tools/opt/opt.cpp
index 5fe2f03..4ef92e1 100644
--- a/tools/opt/opt.cpp
+++ b/tools/opt/opt.cpp
@@ -50,6 +50,8 @@
 #include "llvm/Support/TargetSelect.h"
 #include "llvm/Support/ToolOutputFile.h"
 #include "llvm/Target/TargetMachine.h"
+#include "llvm/Transforms/EnterLeave.h"
+#include "llvm/Transforms/EnterLeaveMapParser.h"
 #include "llvm/Transforms/IPO/PassManagerBuilder.h"
 #include <algorithm>
 #include <memory>
@@ -77,6 +79,11 @@ static cl::opt<std::string>
 InputFilename(cl::Positional, cl::desc("<input bitcode file>"),
     cl::init("-"), cl::value_desc("filename"));
 
+static cl::list<std::string> EnterLeaveMapFiles(
+    "enter-leave-map",
+    cl::desc("Mapping files for enter/leave instrumentation"),
+    cl::value_desc("filename"));
+
 static cl::opt<std::string>
 OutputFilename("o", cl::desc("Override output filename"),
                cl::value_desc("filename"));
@@ -337,6 +344,17 @@ int main(int argc, char **argv) {
     return 1;
   }
 
+  EnterLeaveMapParser::ParseResult ELMPR =
+    EnterLeaveMapParser::parseEnterLeaveMapFiles(EnterLeaveMapFiles);
+  if (ELMPR.isSuccessfulParse())
+    EnterLeave::setMapping(*ELMPR.Mapping);
+  else {
+    errs() << "Error parsing enter/leave map files.\n";
+    for (const auto &Error : ELMPR.Diagnostics)
+      Error.print(argv[0], errs());
+    return 1;
+  }
+
   SMDiagnostic Err;
 
   // Load the input module...
-- 
2.4.6



More information about the llvm-commits mailing list