1//===- JITEventListenerTestCommon.h - Helper for JITEventListener tests ------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===-------------------------------------------------------------------------------===//
9
10#ifndef JIT_EVENT_LISTENER_TEST_COMMON_H
11#define JIT_EVENT_LISTENER_TEST_COMMON_H
12
13#include "llvm/CodeGen/MachineCodeInfo.h"
14#include "llvm/Config/config.h"
15#include "llvm/DIBuilder.h"
16#include "llvm/DebugInfo.h"
17#include "llvm/ExecutionEngine/JIT.h"
18#include "llvm/ExecutionEngine/JITEventListener.h"
19#include "llvm/IR/IRBuilder.h"
20#include "llvm/IR/Instructions.h"
21#include "llvm/IR/Module.h"
22#include "llvm/IR/TypeBuilder.h"
23#include "llvm/Support/Dwarf.h"
24#include "llvm/Support/TargetSelect.h"
25#include "gtest/gtest.h"
26#include <string>
27#include <utility>
28#include <vector>
29
30typedef std::vector<std::pair<std::string, unsigned int> > SourceLocations;
31typedef std::map<uint64_t, SourceLocations> NativeCodeMap;
32
33class JITEnvironment : public testing::Environment {
34  virtual void SetUp() {
35    // Required to create a JIT.
36    llvm::InitializeNativeTarget();
37  }
38};
39
40inline unsigned int getLine() {
41  return 12;
42}
43
44inline unsigned int getCol() {
45  return 0;
46}
47
48inline const char* getFilename() {
49  return "mock_source_file.cpp";
50}
51
52// Test fixture shared by tests for listener implementations
53template<typename WrapperT>
54class JITEventListenerTestBase : public testing::Test {
55protected:
56  llvm::OwningPtr<WrapperT> MockWrapper;
57  llvm::OwningPtr<llvm::JITEventListener> Listener;
58
59public:
60  llvm::Module* M;
61  llvm::MDNode* Scope;
62  llvm::ExecutionEngine* EE;
63  llvm::DIBuilder* DebugBuilder;
64  llvm::IRBuilder<> Builder;
65
66  JITEventListenerTestBase(WrapperT* w)
67  : MockWrapper(w)
68  , M(new llvm::Module("module", llvm::getGlobalContext()))
69  , EE(llvm::EngineBuilder(M)
70    .setEngineKind(llvm::EngineKind::JIT)
71    .setOptLevel(llvm::CodeGenOpt::None)
72    .create())
73  , DebugBuilder(new llvm::DIBuilder(*M))
74  , Builder(llvm::getGlobalContext())
75  {
76    DebugBuilder->createCompileUnit(llvm::dwarf::DW_LANG_C_plus_plus,
77                                    "JIT",
78                                    "JIT",
79                                    "JIT",
80                                    true,
81                                    "",
82                                    1);
83
84    Scope = DebugBuilder->createFile(getFilename(), ".");
85  }
86
87  llvm::Function *buildFunction(const SourceLocations& DebugLocations) {
88    using namespace llvm;
89
90    LLVMContext& GlobalContext = getGlobalContext();
91
92    SourceLocations::const_iterator CurrentDebugLocation
93      = DebugLocations.begin();
94
95    if (CurrentDebugLocation != DebugLocations.end()) {
96      DebugLoc DebugLocation = DebugLoc::get(getLine(), getCol(),
97          DebugBuilder->createFile(CurrentDebugLocation->first, "."));
98      Builder.SetCurrentDebugLocation(DebugLocation);
99      CurrentDebugLocation++;
100    }
101
102    Function *Result = Function::Create(
103        TypeBuilder<int32_t(int32_t), false>::get(GlobalContext),
104        GlobalValue::ExternalLinkage, "id", M);
105    Value *Arg = Result->arg_begin();
106    BasicBlock *BB = BasicBlock::Create(M->getContext(), "entry", Result);
107    Builder.SetInsertPoint(BB);
108    Value* one = ConstantInt::get(GlobalContext, APInt(32, 1));
109    for(; CurrentDebugLocation != DebugLocations.end();
110        ++CurrentDebugLocation) {
111      Arg = Builder.CreateMul(Arg, Builder.CreateAdd(Arg, one));
112      Builder.SetCurrentDebugLocation(
113        DebugLoc::get(CurrentDebugLocation->second, 0,
114                      DebugBuilder->createFile(CurrentDebugLocation->first, ".")));
115    }
116    Builder.CreateRet(Arg);
117    return Result;
118  }
119
120  void TestNoDebugInfo(NativeCodeMap& ReportedDebugFuncs) {
121    SourceLocations DebugLocations;
122    llvm::Function* f = buildFunction(DebugLocations);
123    EXPECT_TRUE(0 != f);
124
125    //Cause JITting and callbacks to our listener
126    EXPECT_TRUE(0 != EE->getPointerToFunction(f));
127    EXPECT_TRUE(1 == ReportedDebugFuncs.size());
128
129    EE->freeMachineCodeForFunction(f);
130    EXPECT_TRUE(ReportedDebugFuncs.size() == 0);
131  }
132
133  void TestSingleLine(NativeCodeMap& ReportedDebugFuncs) {
134    SourceLocations DebugLocations;
135    DebugLocations.push_back(std::make_pair(std::string(getFilename()),
136                                            getLine()));
137    llvm::Function* f = buildFunction(DebugLocations);
138    EXPECT_TRUE(0 != f);
139
140    EXPECT_TRUE(0 != EE->getPointerToFunction(f));
141    EXPECT_TRUE(1 == ReportedDebugFuncs.size());
142    EXPECT_STREQ(ReportedDebugFuncs.begin()->second.begin()->first.c_str(),
143                 getFilename());
144    EXPECT_EQ(ReportedDebugFuncs.begin()->second.begin()->second, getLine());
145
146    EE->freeMachineCodeForFunction(f);
147    EXPECT_TRUE(ReportedDebugFuncs.size() == 0);
148  }
149
150  void TestMultipleLines(NativeCodeMap& ReportedDebugFuncs) {
151    using namespace std;
152
153    SourceLocations DebugLocations;
154    unsigned int c = 5;
155    for(unsigned int i = 0; i < c; ++i) {
156      DebugLocations.push_back(make_pair(string(getFilename()), getLine() + i));
157    }
158
159    llvm::Function* f = buildFunction(DebugLocations);
160    EXPECT_TRUE(0 != f);
161
162    EXPECT_TRUE(0 != EE->getPointerToFunction(f));
163    EXPECT_TRUE(1 == ReportedDebugFuncs.size());
164    SourceLocations& FunctionInfo = ReportedDebugFuncs.begin()->second;
165    EXPECT_EQ(c, FunctionInfo.size());
166
167    int VerifyCount = 0;
168    for(SourceLocations::iterator i = FunctionInfo.begin();
169        i != FunctionInfo.end();
170        ++i) {
171      EXPECT_STREQ(i->first.c_str(), getFilename());
172      EXPECT_EQ(i->second, getLine() + VerifyCount);
173      VerifyCount++;
174    }
175
176    EE->freeMachineCodeForFunction(f);
177    EXPECT_TRUE(ReportedDebugFuncs.size() == 0);
178  }
179
180  void TestMultipleFiles(NativeCodeMap& ReportedDebugFuncs) {
181
182    std::string secondFilename("another_file.cpp");
183
184    SourceLocations DebugLocations;
185    DebugLocations.push_back(std::make_pair(std::string(getFilename()),
186                                            getLine()));
187    DebugLocations.push_back(std::make_pair(secondFilename, getLine()));
188    llvm::Function* f = buildFunction(DebugLocations);
189    EXPECT_TRUE(0 != f);
190
191    EXPECT_TRUE(0 != EE->getPointerToFunction(f));
192    EXPECT_TRUE(1 == ReportedDebugFuncs.size());
193    SourceLocations& FunctionInfo = ReportedDebugFuncs.begin()->second;
194    EXPECT_TRUE(2 == FunctionInfo.size());
195
196    EXPECT_STREQ(FunctionInfo.at(0).first.c_str(), getFilename());
197    EXPECT_STREQ(FunctionInfo.at(1).first.c_str(), secondFilename.c_str());
198
199    EXPECT_EQ(FunctionInfo.at(0).second, getLine());
200    EXPECT_EQ(FunctionInfo.at(1).second, getLine());
201
202    EE->freeMachineCodeForFunction(f);
203    EXPECT_TRUE(ReportedDebugFuncs.size() == 0);
204  }
205};
206
207#endif //JIT_EVENT_LISTENER_TEST_COMMON_H
208