Skip to content

Commit 9a92872

Browse files
new valgrind/callgrind profiling script
1 parent 59654c6 commit 9a92872

2 files changed

Lines changed: 1162 additions & 0 deletions

File tree

scripts/profiling/README.md

Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
1+
# macOS Build And Callgrind Profiling
2+
3+
This guide is for building FreeRADIUS in developer mode with profiling symbols on macOS, then running a timed `valgrind --tool=callgrind` profile.
4+
5+
Run every command below from the repository root.
6+
7+
## 1. Prerequisites
8+
9+
Make sure these tools are available before you start:
10+
11+
- Xcode command line tools
12+
- `valgrind`
13+
- `qcachegrind`
14+
- `gmake` or `make`
15+
- `dsymutil`
16+
17+
Quick checks:
18+
19+
```bash
20+
xcode-select -p
21+
command -v valgrind
22+
command -v qcachegrind
23+
command -v gmake || command -v make
24+
command -v dsymutil
25+
```
26+
27+
If you need to install the macOS build dependencies used by this repository, review and run:
28+
29+
```bash
30+
scripts/osx/install_deps.sh
31+
```
32+
33+
## 2. Build And Profile Workflow
34+
35+
This is the recommended workflow when you want one build step and a separate timed profiling step.
36+
37+
### Step 1: Build Only In Developer Mode With Profiling Symbols
38+
39+
```bash
40+
scripts/profiling/profile-callgrind.sh \
41+
--clean \
42+
--build-only \
43+
--cflags "-g3 -O1 -fno-omit-frame-pointer" \
44+
--ldflags "-fno-omit-frame-pointer" \
45+
--jobs "$(sysctl -n hw.ncpu)"
46+
```
47+
48+
Equivalent shell commands without using `profile-callgrind.sh`:
49+
50+
```bash
51+
xcrun gmake distclean || true
52+
rm -rf build
53+
xcrun ./configure \
54+
--enable-developer \
55+
--disable-verify-ptr \
56+
CFLAGS="-g3 -O1 -fno-omit-frame-pointer" \
57+
LDFLAGS="-fno-omit-frame-pointer"
58+
xcrun gmake -j"$(sysctl -n hw.ncpu)"
59+
```
60+
61+
### Step 2: Run A Timed Callgrind Profile With An Explicit Env File
62+
63+
```bash
64+
scripts/profiling/profile-callgrind.sh \
65+
--profile-only \
66+
--env-file build/tests/multi-server/prof-accept/short_ci/freeradius/profiling-server/proto_load_config.env \
67+
--reset-radiusd-args \
68+
--radiusd-arg -f \
69+
--radiusd-arg -m \
70+
--radiusd-arg -l \
71+
--radiusd-arg stdout \
72+
--radiusd-conf-file raddb/radiusd.conf \
73+
--run-seconds 60
74+
```
75+
76+
Equivalent shell commands without using `profile-callgrind.sh`:
77+
78+
```bash
79+
mkdir -p build/callgrind
80+
stamp="$(date +%Y%m%d-%H%M%S)"
81+
82+
set -a
83+
source build/tests/multi-server/prof-accept/short_ci/freeradius/profiling-server/proto_load_config.env
84+
set +a
85+
86+
dsymutil build/bin/local/radiusd || true
87+
find build -type d -name '*.dSYM' -prune -o -type f \( -name '*.dylib' -o -name '*.so' \) -print0 | \
88+
while IFS= read -r -d '' module_file; do
89+
dsymutil "$module_file" || true
90+
done
91+
92+
valgrind \
93+
--tool=callgrind \
94+
--trace-children=yes \
95+
--separate-threads=yes \
96+
--dump-instr=yes \
97+
--collect-jumps=yes \
98+
--cache-sim=yes \
99+
--branch-sim=yes \
100+
--callgrind-out-file="$PWD/build/callgrind/callgrind.radiusd.${stamp}.%p" \
101+
./scripts/bin/radiusd \
102+
-f \
103+
-m \
104+
-l stdout \
105+
-d "$PWD/raddb" \
106+
-n radiusd &
107+
108+
vg_pid=$!
109+
sleep 60
110+
111+
pgrep -f "callgrind.radiusd.${stamp}.%p" | while IFS= read -r pid; do
112+
kill -INT "$pid" || true
113+
done
114+
115+
wait "$vg_pid" || true
116+
```
117+
118+
### Step 3: Open The Newest Callgrind Output
119+
120+
```bash
121+
latest_file=$(find build/callgrind -maxdepth 1 -type f -name 'callgrind.radiusd.*' -size +0c -print0 | xargs -0 ls -t | head -n 1)
122+
qcachegrind "$latest_file"
123+
```
124+
125+
This step already is the direct shell command that `profile-callgrind.sh` would leave you to run manually.
126+
127+
### Optional: Use The Auto-Detected Default Env File
128+
129+
`--env-file` is optional. If you omit it, `profile-callgrind.sh` automatically sources `build/tests/multi-server/prof-accept/short_ci/freeradius/profiling-server/proto_load_config.env` when that file exists.
130+
131+
```bash
132+
scripts/profiling/profile-callgrind.sh \
133+
--profile-only \
134+
--reset-radiusd-args \
135+
--radiusd-arg -f \
136+
--radiusd-arg -m \
137+
--radiusd-arg -l \
138+
--radiusd-arg stdout \
139+
--radiusd-conf-file raddb/radiusd.conf \
140+
--run-seconds 60
141+
```
142+
143+
Equivalent shell commands without using `profile-callgrind.sh`:
144+
145+
```bash
146+
mkdir -p build/callgrind
147+
stamp="$(date +%Y%m%d-%H%M%S)"
148+
149+
set -a
150+
source build/tests/multi-server/prof-accept/short_ci/freeradius/profiling-server/proto_load_config.env
151+
set +a
152+
153+
dsymutil build/bin/local/radiusd || true
154+
find build -type d -name '*.dSYM' -prune -o -type f \( -name '*.dylib' -o -name '*.so' \) -print0 | \
155+
while IFS= read -r -d '' module_file; do
156+
dsymutil "$module_file" || true
157+
done
158+
159+
valgrind \
160+
--tool=callgrind \
161+
--trace-children=yes \
162+
--separate-threads=yes \
163+
--dump-instr=yes \
164+
--collect-jumps=yes \
165+
--cache-sim=yes \
166+
--branch-sim=yes \
167+
--callgrind-out-file="$PWD/build/callgrind/callgrind.radiusd.${stamp}.%p" \
168+
./scripts/bin/radiusd \
169+
-f \
170+
-m \
171+
-l stdout \
172+
-d "$PWD/raddb" \
173+
-n radiusd &
174+
175+
vg_pid=$!
176+
sleep 60
177+
178+
pgrep -f "callgrind.radiusd.${stamp}.%p" | while IFS= read -r pid; do
179+
kill -INT "$pid" || true
180+
done
181+
182+
wait "$vg_pid" || true
183+
```
184+
185+
## 3. Exact Commands The Script Runs
186+
187+
The script wraps the same basic configure, build, and valgrind steps shown below.
188+
189+
### Configure
190+
191+
```bash
192+
xcrun ./configure \
193+
--enable-developer \
194+
--disable-verify-ptr \
195+
CFLAGS="-g3 -O1 -fno-omit-frame-pointer" \
196+
LDFLAGS="-fno-omit-frame-pointer"
197+
```
198+
199+
### Build
200+
201+
```bash
202+
xcrun gmake -j"$(sysctl -n hw.ncpu)"
203+
```
204+
205+
### Generate dSYM Information
206+
207+
By default the script runs `dsymutil` for the main `radiusd` binary and for built modules so that qcachegrind can resolve symbols more cleanly.
208+
209+
Main binary:
210+
211+
```bash
212+
dsymutil build/bin/local/radiusd
213+
```
214+
215+
If you want to skip dSYM generation, use `--no-dsym`. If you want the main binary dSYM but not module dSYMs, use `--no-dsym-modules`.
216+
217+
### Run Valgrind Callgrind
218+
219+
The script profiles the `scripts/bin/radiusd` wrapper by default, not `build/bin/local/radiusd` directly. The wrapper injects the repository-local dictionary and runtime library setup.
220+
221+
Equivalent valgrind launch for the profile-only example above:
222+
223+
```bash
224+
mkdir -p build/callgrind
225+
stamp="$(date +%Y%m%d-%H%M%S)"
226+
227+
set -a
228+
source build/tests/multi-server/prof-accept/short_ci/freeradius/profiling-server/proto_load_config.env
229+
set +a
230+
231+
valgrind \
232+
--tool=callgrind \
233+
--trace-children=yes \
234+
--separate-threads=yes \
235+
--dump-instr=yes \
236+
--collect-jumps=yes \
237+
--cache-sim=yes \
238+
--branch-sim=yes \
239+
--callgrind-out-file="$PWD/build/callgrind/callgrind.radiusd.${stamp}.%p" \
240+
./scripts/bin/radiusd \
241+
-f \
242+
-m \
243+
-l stdout \
244+
-d "$PWD/raddb" \
245+
-n radiusd
246+
```
247+
248+
## 4. What The Main Profiling Arguments Do
249+
250+
These are the arguments used in the recommended profile-only command.
251+
252+
- `--profile-only` skips configure and build and runs only the profiling phase.
253+
- `--env-file <path>` sources an environment file before starting `radiusd`. This is used to export `TEST_LOADGEN_*` variables for the `listen load` configuration in `raddb/radiusd.conf`.
254+
- `--reset-radiusd-args` clears the script's default `radiusd` arguments before later `--radiusd-arg` values are appended.
255+
- `--radiusd-arg -f` keeps `radiusd` in the foreground so valgrind follows the live server process.
256+
- `--radiusd-arg -m` preserves the runtime mode you were already using in wrapper-based runs.
257+
- `--radiusd-arg -l` and `--radiusd-arg stdout` send server logs to standard output.
258+
- `--radiusd-conf-file raddb/radiusd.conf` appends `-d <confdir> -n <name>` so `radiusd` uses the repository's `raddb/radiusd.conf` configuration.
259+
- `--run-seconds 60` profiles for 60 seconds, then stops the valgrind processes for that run and writes the callgrind output files.
260+
261+
Useful related options:
262+
263+
- `--clean` removes existing build artifacts before configure and build.
264+
- `--build-only` performs configure and build without launching valgrind.
265+
- `--configure-only` performs only the configure step.
266+
- `--no-dsym` skips all dSYM generation.
267+
- `--no-dsym-modules` keeps dSYM generation for `radiusd` but skips module dSYMs.
268+
- `--open` opens the selected callgrind file in `qcachegrind` at the end of the run.
269+
270+
## 5. Inspect Results
271+
272+
Confirm that a command history log was written:
273+
274+
```bash
275+
ls -t build/callgrind/commands.radiusd.*.log | head -n 1
276+
```
277+
278+
Inspect the newest log:
279+
280+
```bash
281+
latest_log=$(ls -t build/callgrind/commands.radiusd.*.log | head -n 1)
282+
sed -n '1,200p' "$latest_log"
283+
```
284+
285+
Open the newest non-empty callgrind file:
286+
287+
```bash
288+
latest_file=$(find build/callgrind -maxdepth 1 -type f -name 'callgrind.radiusd.*' -size +0c -print0 | xargs -0 ls -t | head -n 1)
289+
qcachegrind "$latest_file"
290+
```

0 commit comments

Comments
 (0)