Skip to content

Commit 3670e6a

Browse files
treapstertru
authored andcommitted
[BOLT][Instrumentation] Add test for append-pid option
Reviewed By: rafauler Differential Revision: https://reviews.llvm.org/D154121 (cherry picked from commit dfc7599)
1 parent 51844c6 commit 3670e6a

File tree

3 files changed

+219
-0
lines changed

3 files changed

+219
-0
lines changed

bolt/test/lit.cfg.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@
7272
if config.gnu_ld:
7373
config.available_features.add("gnu_ld")
7474

75+
if lit.util.which("fuser"):
76+
config.available_features.add("fuser")
77+
7578
llvm_config.use_default_substitutions()
7679

7780
llvm_config.config.environment["CLANG"] = config.bolt_clang
Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
// Check that indirect call hash tables properly register multiple calls,
2+
// and that calls from different processes don't get mixed up when using
3+
// --instrumentation-file-append-pid.
4+
5+
#include <stdio.h>
6+
#include <stdlib.h>
7+
#include <unistd.h>
8+
9+
__attribute__((noinline)) void funcA(int pid) { printf("funcA %d\n", pid); }
10+
__attribute__((noinline)) void funcB(int pid) { printf("funcB %d\n", pid); }
11+
__attribute__((noinline)) void funcC(int pid) { printf("funcC %d\n", pid); }
12+
__attribute__((noinline)) void funcD(int pid) { printf("funcD %d\n", pid); }
13+
__attribute__((noinline)) void funcE(int pid) { printf("funcE %d\n", pid); }
14+
__attribute__((noinline)) void funcF(int pid) { printf("funcF %d\n", pid); }
15+
__attribute__((noinline)) void funcG(int pid) { printf("funcG %d\n", pid); }
16+
__attribute__((noinline)) void funcH(int pid) { printf("funcH %d\n", pid); }
17+
__attribute__((noinline)) void funcI(int pid) { printf("funcI %d\n", pid); }
18+
__attribute__((noinline)) void funcJ(int pid) { printf("funcJ %d\n", pid); }
19+
__attribute__((noinline)) void funcK(int pid) { printf("funcK %d\n", pid); }
20+
__attribute__((noinline)) void funcL(int pid) { printf("funcL %d\n", pid); }
21+
__attribute__((noinline)) void funcM(int pid) { printf("funcM %d\n", pid); }
22+
__attribute__((noinline)) void funcN(int pid) { printf("funcN %d\n", pid); }
23+
__attribute__((noinline)) void funcO(int pid) { printf("funcO %d\n", pid); }
24+
__attribute__((noinline)) void funcP(int pid) { printf("funcP %d\n", pid); }
25+
26+
int main() {
27+
28+
void (*funcs[])(int) = {funcA, funcB, funcC, funcD, funcE, funcF,
29+
funcG, funcH, funcI, funcJ, funcK, funcL,
30+
funcM, funcN, funcO, funcP};
31+
int i;
32+
33+
switch (fork()) {
34+
case -1:
35+
printf("Failed to fork!\n");
36+
exit(-1);
37+
break;
38+
case 0:
39+
i = 0;
40+
break;
41+
default:
42+
i = 1;
43+
break;
44+
}
45+
int pid = getpid();
46+
for (; i < sizeof(funcs) / sizeof(void *); i += 2) {
47+
funcs[i](pid);
48+
}
49+
50+
return 0;
51+
}
52+
/*
53+
REQUIRES: system-linux,shell,fuser
54+
55+
RUN: %clang %cflags %s -o %t.exe -Wl,-q -pie -fpie
56+
57+
RUN: llvm-bolt %t.exe --instrument --instrumentation-file=%t.fdata \
58+
RUN: --conservative-instrumentation -o %t.instrumented_conservative \
59+
RUN: --instrumentation-sleep-time=1 --instrumentation-no-counters-clear \
60+
RUN: --instrumentation-wait-forks
61+
62+
# Instrumented program needs to finish returning zero
63+
# Both output and profile must contain all 16 functions
64+
RUN: %t.instrumented_conservative > %t.output
65+
# Wait for profile and output to be fully written
66+
RUN: bash %S/wait_file.sh %t.output
67+
RUN: bash %S/wait_file.sh %t.fdata
68+
RUN: cat %t.output | FileCheck %s --check-prefix=CHECK-OUTPUT
69+
RUN: cat %t.fdata | FileCheck %s --check-prefix=CHECK-COMMON-PROF
70+
71+
CHECK-OUTPUT-DAG: funcA
72+
CHECK-OUTPUT-DAG: funcB
73+
CHECK-OUTPUT-DAG: funcC
74+
CHECK-OUTPUT-DAG: funcD
75+
CHECK-OUTPUT-DAG: funcE
76+
CHECK-OUTPUT-DAG: funcF
77+
CHECK-OUTPUT-DAG: funcG
78+
CHECK-OUTPUT-DAG: funcH
79+
CHECK-OUTPUT-DAG: funcI
80+
CHECK-OUTPUT-DAG: funcJ
81+
CHECK-OUTPUT-DAG: funcK
82+
CHECK-OUTPUT-DAG: funcL
83+
CHECK-OUTPUT-DAG: funcM
84+
CHECK-OUTPUT-DAG: funcN
85+
CHECK-OUTPUT-DAG: funcO
86+
CHECK-OUTPUT-DAG: funcP
87+
88+
CHECK-COMMON-PROF-DAG: 1 main {{[0-9a-f]+}} 1 funcA 0 0 1
89+
CHECK-COMMON-PROF-DAG: 1 main {{[0-9a-f]+}} 1 funcB 0 0 1
90+
CHECK-COMMON-PROF-DAG: 1 main {{[0-9a-f]+}} 1 funcC 0 0 1
91+
CHECK-COMMON-PROF-DAG: 1 main {{[0-9a-f]+}} 1 funcD 0 0 1
92+
CHECK-COMMON-PROF-DAG: 1 main {{[0-9a-f]+}} 1 funcE 0 0 1
93+
CHECK-COMMON-PROF-DAG: 1 main {{[0-9a-f]+}} 1 funcF 0 0 1
94+
CHECK-COMMON-PROF-DAG: 1 main {{[0-9a-f]+}} 1 funcG 0 0 1
95+
CHECK-COMMON-PROF-DAG: 1 main {{[0-9a-f]+}} 1 funcH 0 0 1
96+
CHECK-COMMON-PROF-DAG: 1 main {{[0-9a-f]+}} 1 funcI 0 0 1
97+
CHECK-COMMON-PROF-DAG: 1 main {{[0-9a-f]+}} 1 funcJ 0 0 1
98+
CHECK-COMMON-PROF-DAG: 1 main {{[0-9a-f]+}} 1 funcK 0 0 1
99+
CHECK-COMMON-PROF-DAG: 1 main {{[0-9a-f]+}} 1 funcL 0 0 1
100+
CHECK-COMMON-PROF-DAG: 1 main {{[0-9a-f]+}} 1 funcM 0 0 1
101+
CHECK-COMMON-PROF-DAG: 1 main {{[0-9a-f]+}} 1 funcN 0 0 1
102+
CHECK-COMMON-PROF-DAG: 1 main {{[0-9a-f]+}} 1 funcO 0 0 1
103+
CHECK-COMMON-PROF-DAG: 1 main {{[0-9a-f]+}} 1 funcP 0 0 1
104+
105+
RUN: llvm-bolt %t.exe --instrument --instrumentation-file=%t \
106+
RUN: --instrumentation-file-append-pid \
107+
RUN: -o %t.instrumented
108+
109+
RUN: %t.instrumented > %t.output
110+
# Wait till output is fully written in case child outlives parent
111+
RUN: bash %S/wait_file.sh %t.output
112+
# Make sure all functions were called
113+
RUN: cat %t.output | FileCheck %s --check-prefix=CHECK-OUTPUT
114+
115+
RUN: child_pid=$(cat %t.output | grep funcA | awk '{print $2;}')
116+
RUN: par_pid=$(cat %t.output | grep funcB | awk '{print $2;}')
117+
118+
RUN: bash %S/wait_file.sh %t.$child_pid.fdata
119+
RUN: bash %S/wait_file.sh %t.$par_pid.fdata
120+
121+
RUN: mv %t.$child_pid.fdata %t.child.fdata
122+
RUN: mv %t.$par_pid.fdata %t.parent.fdata
123+
124+
# Instrumented binary must produce two profiles with only local calls
125+
# recorded. Functions called only in child should not appear in parent's
126+
# process and vice versa.
127+
RUN: cat %t.child.fdata | FileCheck %s --check-prefix=CHECK-CHILD
128+
RUN: cat %t.child.fdata | FileCheck %s --check-prefix=CHECK-NOCHILD
129+
RUN: cat %t.parent.fdata | FileCheck %s --check-prefix=CHECK-PARENT
130+
RUN: cat %t.parent.fdata | FileCheck %s --check-prefix=CHECK-NOPARENT
131+
132+
CHECK-CHILD-DAG: 1 main {{[0-9a-f]+}} 1 funcA 0 0 1
133+
CHECK-CHILD-DAG: 1 main {{[0-9a-f]+}} 1 funcC 0 0 1
134+
CHECK-CHILD-DAG: 1 main {{[0-9a-f]+}} 1 funcE 0 0 1
135+
CHECK-CHILD-DAG: 1 main {{[0-9a-f]+}} 1 funcG 0 0 1
136+
CHECK-CHILD-DAG: 1 main {{[0-9a-f]+}} 1 funcI 0 0 1
137+
CHECK-CHILD-DAG: 1 main {{[0-9a-f]+}} 1 funcK 0 0 1
138+
CHECK-CHILD-DAG: 1 main {{[0-9a-f]+}} 1 funcM 0 0 1
139+
CHECK-CHILD-DAG: 1 main {{[0-9a-f]+}} 1 funcO 0 0 1
140+
141+
CHECK-NOCHILD-NOT: funcB
142+
CHECK-NOCHILD-NOT: funcD
143+
CHECK-NOCHILD-NOT: funcF
144+
CHECK-NOCHILD-NOT: funcH
145+
CHECK-NOCHILD-NOT: funcJ
146+
CHECK-NOCHILD-NOT: funcL
147+
CHECK-NOCHILD-NOT: funcN
148+
CHECK-NOCHILD-NOT: funcP
149+
150+
CHECK-PARENT-DAG: 1 main {{[0-9a-f]+}} 1 funcB 0 0 1
151+
CHECK-PARENT-DAG: 1 main {{[0-9a-f]+}} 1 funcD 0 0 1
152+
CHECK-PARENT-DAG: 1 main {{[0-9a-f]+}} 1 funcF 0 0 1
153+
CHECK-PARENT-DAG: 1 main {{[0-9a-f]+}} 1 funcH 0 0 1
154+
CHECK-PARENT-DAG: 1 main {{[0-9a-f]+}} 1 funcJ 0 0 1
155+
CHECK-PARENT-DAG: 1 main {{[0-9a-f]+}} 1 funcL 0 0 1
156+
CHECK-PARENT-DAG: 1 main {{[0-9a-f]+}} 1 funcN 0 0 1
157+
CHECK-PARENT-DAG: 1 main {{[0-9a-f]+}} 1 funcP 0 0 1
158+
159+
CHECK-NOPARENT-NOT: funcA
160+
CHECK-NOPARENT-NOT: funcC
161+
CHECK-NOPARENT-NOT: funcE
162+
CHECK-NOPARENT-NOT: funcG
163+
CHECK-NOPARENT-NOT: funcI
164+
CHECK-NOPARENT-NOT: funcK
165+
CHECK-NOPARENT-NOT: funcM
166+
CHECK-NOPARENT-NOT: funcO
167+
168+
*/

bolt/test/runtime/wait_file.sh

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
#!/bin/bash
2+
3+
check_file() {
4+
local file="$1"
5+
if [ -z "$file" ]; then
6+
echo "No file passed!"
7+
exit 1
8+
fi
9+
if [ ! -f "$file" ]; then
10+
return 1
11+
fi
12+
13+
fuser -s "$file"
14+
local ret=$?
15+
if [ $ret -eq 1 ]; then # noone has file open
16+
return 0
17+
fi
18+
if [ $ret -eq 0 ]; then # file open by some processes
19+
return 1
20+
fi
21+
if [ $ret -eq 127 ]; then
22+
echo "fuser command not found!"
23+
exit 1
24+
fi
25+
26+
echo "Unexpected exit code $ret from fuser!"
27+
exit 1
28+
}
29+
30+
wait_file() {
31+
local file="$1"
32+
local max_sleep=10
33+
check_file "$file"
34+
local ret=$?
35+
while [ $ret -ne 0 ] && [ $max_sleep -ne 0 ]; do
36+
sleep 1
37+
max_sleep=$((max_sleep - 1))
38+
check_file $file
39+
ret=$?
40+
done
41+
if [ $max_sleep -eq 0 ]; then
42+
echo "The file does not exist or the test hung!"
43+
exit 1
44+
fi
45+
46+
}
47+
file="$1"
48+
wait_file "$file"

0 commit comments

Comments
 (0)