Skip to content

Commit 1921df4

Browse files
authored
Track caller/callee location and use fewer access calls (#40)
* track caller and callee locations * add parsing of traces * fix some stuff and add first data * add first results for instrumented * use fewer access calls
1 parent 076f61d commit 1921df4

File tree

10 files changed

+281
-31
lines changed

10 files changed

+281
-31
lines changed
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
2+
from argparse import ArgumentParser
3+
from utils import demangle_function_name
4+
5+
def unique_funcs(funcs_per_module):
6+
unique_functions = set()
7+
for module, funcs in funcs_per_module.items():
8+
unique_functions.update(funcs)
9+
return unique_functions
10+
11+
12+
def parse_called_funcs(filename):
13+
print(f'Parsing {filename}')
14+
15+
funcs_per_module = {}
16+
# open the file and read the lines
17+
try:
18+
with open(filename, 'r') as f:
19+
for line in f:
20+
splitline = line.strip().split(' ')
21+
if not len(splitline) == 4:
22+
continue
23+
func = splitline[0]
24+
module = splitline[3]
25+
if module not in funcs_per_module:
26+
funcs_per_module[module] = []
27+
funcs_per_module[module].append(func)
28+
except FileNotFoundError:
29+
print(f'File {filename} not found')
30+
return []
31+
32+
return funcs_per_module
33+
34+
def get_unique_funcs(filename):
35+
funcs_per_module = parse_called_funcs(filename)
36+
return unique_funcs(funcs_per_module)
37+
38+
def get_stored_funcs_dict(filename):
39+
file = open(filename, "r")
40+
funcs_per_module = eval(file.read())
41+
42+
return funcs_per_module
43+
44+
def print_stats(funcs_per_module):
45+
46+
unique_functions = unique_funcs(funcs_per_module)
47+
48+
print(f'Found {len(funcs_per_module)} modules')
49+
50+
print(f'Found {len(unique_functions)} unique functions')
51+
52+
def build_folder_hierarchy(modules):
53+
hierarchy = {}
54+
for module in modules:
55+
module = module.removeprefix('/home/webmiche/questions/llvm-project/')
56+
parts = module.split('/')
57+
current = hierarchy
58+
for part in parts:
59+
if part not in current:
60+
current[part] = {}
61+
current = current[part]
62+
return hierarchy
63+
64+
def print_hierarchy(hierarchy):
65+
for outer_most, inner in hierarchy.items():
66+
print(outer_most)
67+
for inner_most, inner_inner in inner.items():
68+
print(f' {inner_most}')
69+
for inner_inner_most in inner_inner:
70+
print(f' {inner_inner_most}')
71+
for inner_inner_inner in inner_inner[inner_inner_most]:
72+
print(f' {inner_inner_inner}')
73+
for inner_inner_inner_inner in inner_inner[inner_inner_most][inner_inner_inner]:
74+
print(f' {inner_inner_inner_inner}')
75+
print()
76+
print()
77+
print()
78+
79+
80+
81+
if __name__ == '__main__':
82+
# pass the filename as an argument
83+
parser = ArgumentParser()
84+
parser.add_argument('filename')
85+
args = parser.parse_args()
86+
filename = args.filename
87+
funcs_per_module = get_stored_funcs_dict(filename)
88+
89+
90+
hierarchy = build_folder_hierarchy(funcs_per_module.keys())
91+
92+
print_hierarchy(hierarchy)
93+
94+
# add up all the functions in the TableGen folder
95+
functions_in_tablegen = {}
96+
for module, funcs in funcs_per_module.items():
97+
if module.startswith('/home/webmiche/questions/llvm-project/llvm/utils/TableGen'):
98+
functions_in_tablegen[module] = funcs
99+
100+
unique_functions = unique_funcs(functions_in_tablegen)
101+
total_functions = len(unique_functions)
102+
103+
104+
print_stats(funcs_per_module)
105+
106+
print(f'Functions in TableGen: {total_functions}')

CFFunctionInstrumentation/parse_trace.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
from collections import Counter
33
from subprocess import run, PIPE
4+
from utils import demangle_function_name
45

56
def parse_trace(trace_string) -> dict:
67

@@ -30,10 +31,6 @@ def parse_file(file_path) -> dict:
3031
trace_string = f.read()
3132
return parse_trace(trace_string)
3233

33-
def demangle_function_name(function_name):
34-
cmd = f"llvm-cxxfilt {function_name}"
35-
result = run(cmd, shell=True, stdout=PIPE)
36-
return result.stdout.decode('utf-8').strip()
3734

3835
def single_valued_functions(trace_dict):
3936
result = {}

CFFunctionInstrumentation/utils.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
from subprocess import run, PIPE
2+
3+
def demangle_function_name(function_name):
4+
cmd = f"llvm-cxxfilt {function_name}"
5+
result = run(cmd, shell=True, stdout=PIPE)
6+
return result.stdout.decode('utf-8').strip()

funcs_per_module.zip

13.1 MB
Binary file not shown.

instrumented_per_module.zip

7.29 MB
Binary file not shown.

llvm/include/llvm/Transforms/Utils/CFFunctionInstrumentation.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "llvm/IR/PassManager.h"
99
#include "llvm/Support/Debug.h"
1010
#include "llvm/Transforms/Utils/BasicBlockUtils.h"
11+
#include <fstream>
1112

1213
namespace llvm {
1314

llvm/lib/Analysis/CFFunctionAnalysis.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ PreservedAnalyses CFFunctionAnalysisStorePass::run(Module &M,
118118
}
119119

120120
for (auto &F : CalledFunctions) {
121-
out << F.str() << "\n";
121+
out << F.str() << " called from " << M.getName().str() << "\n";
122122
}
123123

124124
out.close();

llvm/lib/Transforms/Utils/CFFunctionInstrumentation.cpp

Lines changed: 76 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,30 @@ CFFunctionInstrumentationPass::run(Module &M, ModuleAnalysisManager &AM) {
3030
continue;
3131
}
3232

33+
// print the function name and the module name to traced_functions.txt
34+
std::ofstream out;
35+
out.open("traced_functions.txt", std::ios::app);
36+
37+
if (!out) {
38+
errs() << "Error: cannot open file traced_functions.txt \n";
39+
return PreservedAnalyses::none();
40+
}
41+
out << F.getName().str() << " instrumented in " << M.getName().str()
42+
<< "\n";
43+
out.close();
44+
3345
std::string outputString = F.getName().str() + " %lld\n";
3446
StringRef funcFormatStr = StringRef(outputString);
3547
std::string fileName = "function_trace.txt";
3648
StringRef funcFileName = StringRef(fileName);
3749
// for all return instructions, print the return value to a file with the
3850
// name of the function
3951

52+
auto GV = new GlobalVariable(
53+
M, Type::getInt32Ty(M.getContext()), true,
54+
GlobalValue::LinkageTypes::PrivateLinkage,
55+
ConstantInt::get(IntegerType::getInt32Ty(M.getContext()), 0),
56+
"init" + F.getName());
4057
// store already handled blocks
4158
std::set<BasicBlock *> HandledBlocks;
4259
for (auto &BB : F) {
@@ -46,7 +63,7 @@ CFFunctionInstrumentationPass::run(Module &M, ModuleAnalysisManager &AM) {
4663
// Do NOT reinstrument the inserted blocks
4764
if (BB.getName() == "return" || BB.getName() == "print" ||
4865
BB.getName() == "open") {
49-
HandledBlocks.insert(&BB);
66+
HandledBlocks.insert(&BB);
5067
continue;
5168
}
5269
if (auto *RI = dyn_cast<ReturnInst>(BB.getTerminator())) {
@@ -76,10 +93,44 @@ CFFunctionInstrumentationPass::run(Module &M, ModuleAnalysisManager &AM) {
7693
// split at return
7794
BasicBlock *ReturnBB = BB.splitBasicBlock(RI, "return", false);
7895

96+
BasicBlock *CheckBB =
97+
BasicBlock::Create(M.getContext(), "access_check", &F);
98+
BasicBlock *Check2BB =
99+
BasicBlock::Create(M.getContext(), "no_init_check", &F);
79100
BasicBlock *AccessBB =
80101
BasicBlock::Create(M.getContext(), "access", &F);
102+
BasicBlock *UpdateGVBB =
103+
BasicBlock::Create(M.getContext(), "update", &F);
104+
BasicBlock *NoAccessBB =
105+
BasicBlock::Create(M.getContext(), "no_access", &F);
106+
81107
BasicBlock *PrintBB = BasicBlock::Create(M.getContext(), "print", &F);
82108

109+
LLVM_DEBUG(dbgs() << "Created BBs\n");
110+
111+
IRBuilder<> CheckBuilder(CheckBB);
112+
113+
Value *GV_value = CheckBuilder.CreateLoad(
114+
IntegerType::getInt32Ty(M.getContext()), GV);
115+
116+
// check if GV is 0 (not yet accessed) or -1 (no access) or 1 (access)
117+
Value *CmpGV = CheckBuilder.CreateICmpEQ(
118+
GV_value,
119+
ConstantInt::get(IntegerType::getInt32Ty(M.getContext()), 1));
120+
121+
CheckBuilder.CreateCondBr(CmpGV, PrintBB, Check2BB);
122+
123+
LLVM_DEBUG(dbgs() << "Created check BB\n");
124+
125+
IRBuilder<> Check2Builder(Check2BB);
126+
Value *CmpGV2 = Check2Builder.CreateICmpEQ(
127+
GV_value,
128+
ConstantInt::get(IntegerType::getInt32Ty(M.getContext()), -1));
129+
130+
Check2Builder.CreateCondBr(CmpGV2, ReturnBB, AccessBB);
131+
132+
LLVM_DEBUG(dbgs() << "Created check2 BB\n");
133+
83134
IRBuilder<> AccessBuilder(AccessBB);
84135
// insert call to access function with filename and 0
85136
Value *status = AccessBuilder.CreateCall(
@@ -88,7 +139,25 @@ CFFunctionInstrumentationPass::run(Module &M, ModuleAnalysisManager &AM) {
88139
Value *Cmp = AccessBuilder.CreateICmpEQ(
89140
status,
90141
ConstantInt::get(IntegerType::getInt32Ty(M.getContext()), 0));
91-
AccessBuilder.CreateCondBr(Cmp, PrintBB, ReturnBB);
142+
AccessBuilder.CreateCondBr(Cmp, UpdateGVBB, NoAccessBB);
143+
144+
LLVM_DEBUG(dbgs() << "Created access BB\n");
145+
146+
IRBuilder<> NoAccessBuilder(NoAccessBB);
147+
NoAccessBuilder.CreateStore(
148+
ConstantInt::get(IntegerType::getInt32Ty(M.getContext()), -1),
149+
GV);
150+
NoAccessBuilder.CreateBr(ReturnBB);
151+
152+
LLVM_DEBUG(dbgs() << "Created no access BB\n");
153+
154+
IRBuilder<> UpdateGVBuilder(UpdateGVBB);
155+
UpdateGVBuilder.CreateStore(
156+
ConstantInt::get(IntegerType::getInt32Ty(M.getContext()), 1), GV);
157+
158+
UpdateGVBuilder.CreateBr(PrintBB);
159+
160+
LLVM_DEBUG(dbgs() << "Created update BB\n");
92161

93162
IRBuilder<> PrintBuilder(PrintBB);
94163
FunctionCallee PrintFunc = M.getOrInsertFunction(
@@ -119,14 +188,18 @@ CFFunctionInstrumentationPass::run(Module &M, ModuleAnalysisManager &AM) {
119188
PrintBuilder.CreateCall(CloseFunc, write_fptr);
120189
PrintBuilder.CreateBr(ReturnBB);
121190

122-
BB.getTerminator()->setSuccessor(0, AccessBB);
191+
BB.getTerminator()->setSuccessor(0, CheckBB);
123192

124193
// place new BBs in the correct order
125194
ReturnBB->moveAfter(PrintBB);
126195

127196
HandledBlocks.insert(AccessBB);
128197
HandledBlocks.insert(PrintBB);
129198
HandledBlocks.insert(ReturnBB);
199+
HandledBlocks.insert(CheckBB);
200+
HandledBlocks.insert(Check2BB);
201+
HandledBlocks.insert(UpdateGVBB);
202+
HandledBlocks.insert(NoAccessBB);
130203
}
131204
}
132205
HandledBlocks.insert(&BB);

0 commit comments

Comments
 (0)