mirror of
https://github.com/cirosantilli/linux-kernel-module-cheat.git
synced 2026-01-23 02:05:57 +01:00
trace2line: gem5 support
As noted however, it is potentially too slow to be useful. run: unify gem5 and qemu tracing under -T readme: overhaul tracing documentation from what I've learnt from trace2line
This commit is contained in:
100
README.adoc
100
README.adoc
@@ -1150,6 +1150,8 @@ Maybe it is because they are being copied around at specific locations instead o
|
|||||||
|
|
||||||
See also: https://stackoverflow.com/questions/2589845/what-are-the-first-operations-that-the-linux-kernel-executes-on-boot
|
See also: https://stackoverflow.com/questions/2589845/what-are-the-first-operations-that-the-linux-kernel-executes-on-boot
|
||||||
|
|
||||||
|
<<gem5-tracing>> with `--debug-flags=Exec` does show the right symbols however! So in the worst case, we can just read their source. Amazing.
|
||||||
|
|
||||||
==== GDB step debug early boot by address
|
==== GDB step debug early boot by address
|
||||||
|
|
||||||
One possibility is to run:
|
One possibility is to run:
|
||||||
@@ -3353,9 +3355,9 @@ You can still send key presses to QEMU however even without the mouse capture, j
|
|||||||
|
|
||||||
=== Tracing
|
=== Tracing
|
||||||
|
|
||||||
QEMU has a mechanism to log all instructions executed to a file.
|
QEMU can log several different events.
|
||||||
|
|
||||||
To do it for the Linux kernel boot we have a helper:
|
The most interesting are events which show instructions that QEMU ran, for which we have a helper:
|
||||||
|
|
||||||
....
|
....
|
||||||
./trace-boot -a x86_64
|
./trace-boot -a x86_64
|
||||||
@@ -3367,6 +3369,20 @@ You can then inspect the instructions with:
|
|||||||
less ./out/x86_64/qemu/trace.txt
|
less ./out/x86_64/qemu/trace.txt
|
||||||
....
|
....
|
||||||
|
|
||||||
|
Enable other specific trace events:
|
||||||
|
|
||||||
|
....
|
||||||
|
./run -T trace1,trace2
|
||||||
|
./qemu-trace2txt -a "$arch"
|
||||||
|
less ./out/x86_64/qemu/trace.txt
|
||||||
|
....
|
||||||
|
|
||||||
|
Get the list of available trace events:
|
||||||
|
|
||||||
|
....
|
||||||
|
./run -T help
|
||||||
|
....
|
||||||
|
|
||||||
This functionality relies on the following setup:
|
This functionality relies on the following setup:
|
||||||
|
|
||||||
* `./configure --enable-trace-backends=simple`. This logs in a binary format to the trace file.
|
* `./configure --enable-trace-backends=simple`. This logs in a binary format to the trace file.
|
||||||
@@ -3395,8 +3411,7 @@ in which the boot appears to hang for a considerable time.
|
|||||||
We can further use Binutils' `addr2line` to get the line that corresponds to each address:
|
We can further use Binutils' `addr2line` to get the line that corresponds to each address:
|
||||||
|
|
||||||
....
|
....
|
||||||
./trace-boot -a x86_64
|
./trace-boot -a x86_64 && ./trace2line -a x86_64
|
||||||
./trace2line -a x86_64
|
|
||||||
less ./out/x86_64/qemu/trace-lines.txt
|
less ./out/x86_64/qemu/trace-lines.txt
|
||||||
....
|
....
|
||||||
|
|
||||||
@@ -3435,43 +3450,66 @@ Alternatively, https://github.com/mozilla/rr[`mozilla/rr`] claims it is able to
|
|||||||
|
|
||||||
gem5 also has a tracing mechanism, as documented at: http://www.gem5.org/Trace_Based_Debugging
|
gem5 also has a tracing mechanism, as documented at: http://www.gem5.org/Trace_Based_Debugging
|
||||||
|
|
||||||
Try it out with:
|
|
||||||
|
|
||||||
....
|
|
||||||
./run -a aarch64 -E 'm5 exit' -G '--debug-flags=Exec' -g
|
|
||||||
....
|
|
||||||
|
|
||||||
The trace file is located at:
|
|
||||||
|
|
||||||
....
|
....
|
||||||
|
./run -a aarch64 -E 'm5 exit' -g -T Exec
|
||||||
less out/aarch64/gem5/m5out/trace.txt
|
less out/aarch64/gem5/m5out/trace.txt
|
||||||
....
|
....
|
||||||
|
|
||||||
but be warned, it is humongous, at 16Gb.
|
List all available debug flags:
|
||||||
|
|
||||||
It made the runtime about 4x slower on the <<p51>>, with or without `.gz` compression.
|
|
||||||
|
|
||||||
The list of available debug flags can be found with:
|
|
||||||
|
|
||||||
....
|
....
|
||||||
./run -a aarch64 -G --debug-help -g
|
./run -a aarch64 -G --debug-help -g
|
||||||
....
|
....
|
||||||
|
|
||||||
but for meaningful descriptions you need to look at the source code:
|
but to understand most of them you have to look at the source code:
|
||||||
|
|
||||||
....
|
....
|
||||||
less gem5/gem5/src/cpu/SConscript
|
less gem5/gem5/src/cpu/SConscript
|
||||||
|
less gem5/gem5/src/cpu/exetrace.cc
|
||||||
....
|
....
|
||||||
|
|
||||||
The default `Exec` format reads symbol names from the Linux kernel image and show them, which is pretty awesome if you ask me.
|
As can be seen on the `Sconstruct`, `Exec` is just an alias that enables a set of flags.
|
||||||
|
|
||||||
TODO can we get just the executed addresses out of gem5? The following gets us closer, but not quite:
|
Be warned, the trace is humongous, at 16Gb.
|
||||||
|
|
||||||
|
We can make the trace smaller by naming the trace file as `trace.txt.gz`, which enables GZIP compression, but that is not currently exposed on our scripts, since you usually just need something human readable to work on.
|
||||||
|
|
||||||
|
Enabling tracing made the runtime about 4x slower on the <<p51>>, with or without `.gz` compression.
|
||||||
|
|
||||||
|
The output format is of type:
|
||||||
|
|
||||||
....
|
....
|
||||||
./run -a aarch64 -E 'm5 exit' -G '--debug-flags=ExecEnable,ExecKernel,ExecUse' -g
|
25007000: system.cpu T0 : @start_kernel : stp
|
||||||
|
25007000: system.cpu T0 : @start_kernel.0 : addxi_uop ureg0, sp, #-112 : IntAlu : D=0xffffff8008913f90
|
||||||
|
25007500: system.cpu T0 : @start_kernel.1 : strxi_uop x29, [ureg0] : MemWrite : D=0x0000000000000000 A=0xffffff8008913f90
|
||||||
|
25008000: system.cpu T0 : @start_kernel.2 : strxi_uop x30, [ureg0, #8] : MemWrite : D=0x0000000000000000 A=0xffffff8008913f98
|
||||||
|
25008500: system.cpu T0 : @start_kernel.3 : addxi_uop sp, ureg0, #0 : IntAlu : D=0xffffff8008913f90
|
||||||
....
|
....
|
||||||
|
|
||||||
We could of course just pipe it to stdout and `awk` it up.
|
There are two types of lines:
|
||||||
|
|
||||||
|
* full instructions, as the first line. Only shown if the `ExecMacro` flag is given.
|
||||||
|
* micro ops that constitute the instruction, the lines that follow. Yes, `aarch64` also has microops: link:https://superuser.com/questions/934752/do-arm-processors-like-cortex-a9-use-microcode/934755#934755[]. Only shown if the `ExecMicro` flag is given.
|
||||||
|
|
||||||
|
Breakdown:
|
||||||
|
|
||||||
|
* `25007500`: time count in some unit. Note how the microops execute at further timestamps.
|
||||||
|
* `system.cpu`: distinguishes between CPUs when there are more than one
|
||||||
|
* `T0`: thread number. TODO: link:https://superuser.com/questions/133082/hyper-threading-and-dual-core-whats-the-difference/995858#995858[hyperthread]? How to play with it?
|
||||||
|
* `@start_kernel`: we are in the `start_kernel` function. Awesome feature! Implemented with libelf https://sourceforge.net/projects/elftoolchain/ copy pasted in-tree `ext/libelf`. To get raw addresses, remove the `ExecSymbol`, which is enabled by `Exec`.
|
||||||
|
* `.1` as in `@start_kernel.1`: index of the microop
|
||||||
|
* `stp`: instruction disassembly. Seems to use `.isa` files dispersed per arch, which is an in house format: http://gem5.org/ISA_description_system
|
||||||
|
* `strxi_uop x29, [ureg0]`: microop disassembly.
|
||||||
|
* `MemWrite : D=0x0000000000000000 A=0xffffff8008913f90`: TODO. Further description of the microops.
|
||||||
|
|
||||||
|
Trace the source lines just like <<trace-source-lines,for QEMU>> with:
|
||||||
|
|
||||||
|
....
|
||||||
|
./trace-boot -a aarch64 -g && ./trace2line -a aarch64 -g
|
||||||
|
less ./out/aarch64/gem5/trace-lines.txt
|
||||||
|
....
|
||||||
|
|
||||||
|
TODO: 7452d399290c9c1fc6366cdad129ef442f323564 `./trace2line` this is too slow and takes hours. QEMU's processing of 170k events takes 7 seconds. gem5's processing is analogous, but there are 140M events, so it should take 7000 seconds ~ 2 hours which seems consistent with what I observe, so maybe there is no way to speed this up... The workaround is to just use gem5's `ExecSymbol` to get function granularity, and then GDB individually if line detail is needed?
|
||||||
|
|
||||||
=== QEMU GUI is unresponsive
|
=== QEMU GUI is unresponsive
|
||||||
|
|
||||||
@@ -4287,6 +4325,24 @@ Pass options to the `gem5` executable itself:
|
|||||||
./run -G '-h' -g
|
./run -G '-h' -g
|
||||||
....
|
....
|
||||||
|
|
||||||
|
=== gem5 exit after a number of instructions
|
||||||
|
|
||||||
|
Quit the simulation after `1024` instructions:
|
||||||
|
|
||||||
|
....
|
||||||
|
./run -g -- -I 1024
|
||||||
|
....
|
||||||
|
|
||||||
|
Can be nicely checked with <<gem5-tracing>>.
|
||||||
|
|
||||||
|
Cycles instead of instructions:
|
||||||
|
|
||||||
|
....
|
||||||
|
./run -g -- -m 1024
|
||||||
|
....
|
||||||
|
|
||||||
|
Otherwise the simulation runs forever by default.
|
||||||
|
|
||||||
=== Run multiple gem5 instances at once
|
=== Run multiple gem5 instances at once
|
||||||
|
|
||||||
gem5 just assigns new ports if some ports are occupied, so we can do:
|
gem5 just assigns new ports if some ports are occupied, so we can do:
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ qemu_insts() (
|
|||||||
arch="$1"
|
arch="$1"
|
||||||
./qemu-trace2txt -a "$arch"
|
./qemu-trace2txt -a "$arch"
|
||||||
set_common_vars "$arch"
|
set_common_vars "$arch"
|
||||||
printf "instructions $(wc -l "${qemu_trace_txt_file}" | cut -d' ' -f1)\n" >> "$common_bench_boot"
|
printf "instructions $(wc -l "${common_trace_txt_file}" | cut -d' ' -f1)\n" >> "$common_bench_boot"
|
||||||
)
|
)
|
||||||
rm -f "${common_bench_boot}"
|
rm -f "${common_bench_boot}"
|
||||||
|
|
||||||
|
|||||||
3
common
3
common
@@ -36,11 +36,12 @@ set_common_vars() {
|
|||||||
gem5_out_dir="${out_arch_dir}/gem5"
|
gem5_out_dir="${out_arch_dir}/gem5"
|
||||||
m5out_dir="${gem5_out_dir}/m5out"
|
m5out_dir="${gem5_out_dir}/m5out"
|
||||||
qemu_out_dir="${out_arch_dir}/qemu"
|
qemu_out_dir="${out_arch_dir}/qemu"
|
||||||
qemu_trace_txt_file="${qemu_out_dir}/trace.txt"
|
|
||||||
if "$gem5"; then
|
if "$gem5"; then
|
||||||
common_out_run_dir="$gem5_out_dir"
|
common_out_run_dir="$gem5_out_dir"
|
||||||
|
common_trace_txt_file="${common_out_run_dir}/m5out/trace.txt"
|
||||||
else
|
else
|
||||||
common_out_run_dir="$qemu_out_dir"
|
common_out_run_dir="$qemu_out_dir"
|
||||||
|
common_trace_txt_file="${common_out_run_dir}/trace.txt"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
root_dir="$(pwd)"
|
root_dir="$(pwd)"
|
||||||
|
|||||||
@@ -13,4 +13,4 @@ set_common_vars "$arch" false
|
|||||||
./qemu/scripts/simpletrace.py \
|
./qemu/scripts/simpletrace.py \
|
||||||
"${build_dir}/host-qemu-custom/trace-events-all" \
|
"${build_dir}/host-qemu-custom/trace-events-all" \
|
||||||
"${qemu_out_dir}/trace.bin" \
|
"${qemu_out_dir}/trace.bin" \
|
||||||
> "${qemu_trace_txt_file}"
|
> "${common_trace_txt_file}"
|
||||||
|
|||||||
12
run
12
run
@@ -28,9 +28,10 @@ nographic=true
|
|||||||
root=
|
root=
|
||||||
tmux=false
|
tmux=false
|
||||||
tmux_args=
|
tmux_args=
|
||||||
|
trace_enabled=false
|
||||||
# A dummy value that is already turned on by default and does not produce large output,
|
# A dummy value that is already turned on by default and does not produce large output,
|
||||||
# just to prevent QEMU from emitting a warning that '' is not valid.
|
# just to prevent QEMU from emitting a warning that '' is not valid.
|
||||||
trace_enable=pr_manager_run
|
trace_type=pr_manager_run
|
||||||
vnc=
|
vnc=
|
||||||
while getopts a:c:DdE:e:F:f:G:ghIiKkm:T:U:uVx OPT; do
|
while getopts a:c:DdE:e:F:f:G:ghIiKkm:T:U:uVx OPT; do
|
||||||
case "$OPT" in
|
case "$OPT" in
|
||||||
@@ -93,7 +94,8 @@ while getopts a:c:DdE:e:F:f:G:ghIiKkm:T:U:uVx OPT; do
|
|||||||
memory="$OPTARG"
|
memory="$OPTARG"
|
||||||
;;
|
;;
|
||||||
T)
|
T)
|
||||||
trace_enable="$OPTARG"
|
trace_enabled=true
|
||||||
|
trace_type="$OPTARG"
|
||||||
;;
|
;;
|
||||||
U)
|
U)
|
||||||
tmux_args="$OPTARG"
|
tmux_args="$OPTARG"
|
||||||
@@ -155,6 +157,10 @@ if "$gem5"; then
|
|||||||
else
|
else
|
||||||
gem5_arch=ARM
|
gem5_arch=ARM
|
||||||
fi
|
fi
|
||||||
|
if "$trace_enabled"; then
|
||||||
|
gem5opts="${gem5opts} --debug-flags='${trace_type}' \\
|
||||||
|
"
|
||||||
|
fi
|
||||||
gem5_common="\
|
gem5_common="\
|
||||||
M5_PATH='${gem5_build_dir}/system' \\
|
M5_PATH='${gem5_build_dir}/system' \\
|
||||||
${debug_vm} \
|
${debug_vm} \
|
||||||
@@ -203,7 +209,7 @@ ${debug_vm} \
|
|||||||
-monitor telnet::45454,server,nowait \\
|
-monitor telnet::45454,server,nowait \\
|
||||||
-netdev user,hostfwd=tcp::45455-:45455,hostfwd=tcp::45456-:22,id=net0 \\
|
-netdev user,hostfwd=tcp::45455-:45455,hostfwd=tcp::45456-:22,id=net0 \\
|
||||||
-smp '${cpus}' \\
|
-smp '${cpus}' \\
|
||||||
-trace 'enable=${trace_enable},file=${qemu_out_dir}/trace.bin' \\
|
-trace 'enable=${trace_type},file=${qemu_out_dir}/trace.bin' \\
|
||||||
-virtfs 'local,path=${p9_dir},mount_tag=host_scratch,security_model=mapped,id=host_scratch' \\
|
-virtfs 'local,path=${p9_dir},mount_tag=host_scratch,security_model=mapped,id=host_scratch' \\
|
||||||
-virtfs 'local,path=${buildroot_out_dir}/build,mount_tag=host_out,security_model=mapped,id=host_out' \\
|
-virtfs 'local,path=${buildroot_out_dir}/build,mount_tag=host_out,security_model=mapped,id=host_out' \\
|
||||||
${vnc}"
|
${vnc}"
|
||||||
|
|||||||
@@ -40,11 +40,12 @@
|
|||||||
The default is the minimum amount that boots all archs without extra
|
The default is the minimum amount that boots all archs without extra
|
||||||
options added. Anything lower will lead some arch to fail to boot.
|
options added. Anything lower will lead some arch to fail to boot.
|
||||||
Any
|
Any
|
||||||
|`-T` | |Enable extra QEMU trace events.
|
|`-T` |`TRACE_TYPES` |Set trace events to be enabled.
|
||||||
`./configure --enable-trace-backends=simple` seems to enable
|
If not given, gem5 tracing is completely disabled, while QEMU tracing
|
||||||
some by default, e.g. `pr_manager_run`, and I don't know how to
|
is enabled but uses default traces that are very rare and don't affect
|
||||||
get rid of them. But those events are rare and should not impact
|
performance. `./configure --enable-trace-backends=simple` seems to enable
|
||||||
performance, so we just ignore them for now.
|
some traces by default, e.g. `pr_manager_run`, and I don't know how to
|
||||||
|
get rid of them.
|
||||||
|`-U` | |Pass extra parameters to the program running on the `-u` tmux split.
|
|`-U` | |Pass extra parameters to the program running on the `-u` tmux split.
|
||||||
|`-u` | |Create a tmUx split the window.
|
|`-u` | |Create a tmUx split the window.
|
||||||
You must already be inside of a `tmux` session to use this option.
|
You must already be inside of a `tmux` session to use this option.
|
||||||
|
|||||||
34
trace-boot
34
trace-boot
@@ -2,22 +2,30 @@
|
|||||||
set -eu
|
set -eu
|
||||||
. common
|
. common
|
||||||
set -- ${cli_trace_boot:-} "$@"
|
set -- ${cli_trace_boot:-} "$@"
|
||||||
while getopts a: OPT; do
|
while getopts a:g OPT; do
|
||||||
case "$OPT" in
|
case "$OPT" in
|
||||||
a)
|
a)
|
||||||
arch="$OPTARG"
|
arch="$OPTARG"
|
||||||
;;
|
;;
|
||||||
|
g)
|
||||||
|
gem5=true
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
set_common_vars "$arch" false
|
shift "$(($OPTIND - 1))"
|
||||||
time ./run -a "$arch" -e 'init=/poweroff.out' -T exec_tb
|
set_common_vars "$arch" "$gem5"
|
||||||
time ./qemu-trace2txt -a "$arch"
|
if "$gem5"; then
|
||||||
|
# Exactly what Exec enabled, but without ExecSymbol and ExecMicro.
|
||||||
# Instruction count.
|
time ./run -a aarch64 -E 'm5 exit' -g -T 'ExecEnable,ExecTicks,ExecOpClass,ExecThread,ExecEffAddr,ExecResult,ExecMacro,ExecFaulting,ExecUser,ExecKernel' "$@"
|
||||||
# We could put this on a separate script, but it just adds more arch boilerplate to a new script.
|
else
|
||||||
# So let's just leave it here for now since it did not add a significant processing time.
|
time ./run -a "$arch" -e 'init=/poweroff.out' -T exec_tb "$@"
|
||||||
echo "instructions $(wc -l "${qemu_trace_txt_file}" | cut -d' ' -f1)"
|
time ./qemu-trace2txt -a "$arch"
|
||||||
entry_addr=$("${root_dir}/runtc" readelf -h "${build_dir}/linux-custom/vmlinux" | grep 'Entry point address' | sed -E 's/.*: *//')
|
# Instruction count.
|
||||||
echo "entry_address ${entry_addr}"
|
# We could put this on a separate script, but it just adds more arch boilerplate to a new script.
|
||||||
sed "/${entry_addr}/q" "${qemu_trace_txt_file}" >"${qemu_out_dir}/trace-boot.txt"
|
# So let's just leave it here for now since it did not add a significant processing time.
|
||||||
echo "instructions_firmware $(wc -l "${qemu_out_dir}/trace-boot.txt" | cut -d' ' -f1)"
|
echo "instructions $(wc -l "${common_trace_txt_file}" | cut -d' ' -f1)"
|
||||||
|
entry_addr=$("${root_dir}/runtc" readelf -h "${build_dir}/linux-custom/vmlinux" | grep 'Entry point address' | sed -E 's/.*: *//')
|
||||||
|
echo "entry_address ${entry_addr}"
|
||||||
|
sed "/${entry_addr}/q" "${common_trace_txt_file}" >"${qemu_out_dir}/trace-boot.txt"
|
||||||
|
echo "instructions_firmware $(wc -l "${qemu_out_dir}/trace-boot.txt" | cut -d' ' -f1)"
|
||||||
|
fi
|
||||||
|
|||||||
17
trace2line
17
trace2line
@@ -2,17 +2,26 @@
|
|||||||
set -eu
|
set -eu
|
||||||
. common
|
. common
|
||||||
set -- ${cli_trace2line:-} "$@"
|
set -- ${cli_trace2line:-} "$@"
|
||||||
while getopts a: OPT; do
|
while getopts a:g OPT; do
|
||||||
case "$OPT" in
|
case "$OPT" in
|
||||||
a)
|
a)
|
||||||
arch="$OPTARG"
|
arch="$OPTARG"
|
||||||
;;
|
;;
|
||||||
|
g)
|
||||||
|
gem5=true
|
||||||
|
;;
|
||||||
esac
|
esac
|
||||||
done
|
done
|
||||||
set_common_vars "$arch" false
|
set_common_vars "$arch" "$gem5"
|
||||||
kernel_dir="${build_dir}/linux-custom"
|
kernel_dir="${build_dir}/linux-custom"
|
||||||
sed -E 's/.*pc=//' "${qemu_out_dir}/trace.txt" | \
|
(
|
||||||
|
if "$gem5"; then
|
||||||
|
sed -r 's/^.* (0x[^. ]*)[. ].*/\1/' "$common_trace_txt_file"
|
||||||
|
else
|
||||||
|
sed -r 's/.*pc=//' "$common_trace_txt_file"
|
||||||
|
fi
|
||||||
|
) | \
|
||||||
xargs "${host_dir}/bin/${arch}-linux-addr2line" -e "${kernel_dir}/vmlinux" -fp | \
|
xargs "${host_dir}/bin/${arch}-linux-addr2line" -e "${kernel_dir}/vmlinux" -fp | \
|
||||||
sed -E "s|at ${kernel_dir}/(\./\|)||" | \
|
sed -E "s|at ${kernel_dir}/(\./\|)||" | \
|
||||||
uniq -c \
|
uniq -c \
|
||||||
> "${qemu_out_dir}/trace-lines.txt"
|
> "${common_out_run_dir}/trace-lines.txt"
|
||||||
|
|||||||
Reference in New Issue
Block a user