While trying to apply puncover to the wifi shell sample from zephyr RTOS[1] on a ESP32C3 (the RISCV one) I ran into a bug:
File ".../puncover/puncover/collector.py", line 438, in add_function_call
if caller not in callee[CALLERS]:
Debugging the issue I found it came from one of the function call instructions.
This is caused, by the more complicated call scheme around this instruction.
There is jal and jalr, the later can be relative to a register, which is the issue.
So the call address cannot be parsed from one single instruction. Cite:
At one level, this post is about two instructions: jal (jump and link) and jalr (jump and link register). But these two instructions are exciting because they enable functions (also known as subroutines or procedure calls).
jal rd, imm # rd = pc+4; pc += imm
jalr rd, rs1, imm # rd = pc+4; pc = rs1+imm
Before updating the PC, a jump instruction writes the address of the following instruction into a register. By saving this return address, we can return to it and continue execution where we left off.
jal uses a 20-bit signed immediate for the jump destination, while jalr uses a register plus 12-bit signed offset in a similar way to the load and store instructions.
jal range is ±1MiB in units of two bytes for greater range with compressed instruction support.
jalr range is ±2KiB, but you can combine jalr with lui or auipc to reach any 32-bit address.
So the culprit assembly line is 4038a354:\t004080e7 jalr 4(ra) # 40000354 <memset>.
Applying the enhance_call_tree_pattern usually takes group 3 of the regex as the call address.
But in this case group 3 is 4, but which means register ra offset by 4.
So correct call path deduction from assembly is much harder on RISCV to implement and remains with...
Exporting the json report and grepping through it cat wifishell.json | grep -e 'jal\\' | wc -l (7181 instances) and cat wifishell.json | grep -e 'jalr' | wc -l (4429 instances) shows both instruction are used - depending how far the function are apart.
GCC output of some includes the callee name, sometimes only the register.
More examples cat wifishell.json | grep jalr:
"4206d1aa:\t778080e7 \tjalr\t1912(ra) # 4038091e <phy_printf>",
"4206d1c8:\t9782 \tjalr\ta5",
"4206d2a2:\t9782 \tjalr\ta5",
"4206d2ba:\t9782 \tjalr\ta5",
"4206d2c8:\t9c02 \tjalr\ts8",
"4206d2d4:\t9782 \tjalr\ta5",
"4206d2e6:\t9782 \tjalr\ta5",
"4206d374:\t9782 \tjalr\ta5",
"4206d3d4:\t9782 \tjalr\ta5",
"4206d3e8:\t9782 \tjalr\ta5",
"4206d3fe:\t9782 \tjalr\ta5",
"4206d412:\t9782 \tjalr\ta5",
"4206d458:\t9782 \tjalr\ta5",
"4206d65c:\t9802 \tjalr\ta6",
"4206d67c:\t2a6080e7 \tjalr\t678(ra) # 4038091e <phy_printf>",
"4206d6ca:\t258080e7 \tjalr\t600(ra) # 4038091e <phy_printf>",
"4206d6f0:\t9802 \tjalr\ta6",
"4206d7b2:\t9802 \tjalr\ta6",
[1] build by west build --sysbuild -p -b xiao_esp32c3/esp32c3 samples/net/wifi/shell/ -- -DCONFIG_STACK_USAGE=y
then puncover --generate-report --report-tag 123 --report-type json --build_dir ~/zephyrproject/zephyr/build/ --elf_file ~/zephyr/build/shell/zephyr/zephyr.elf --report-filename wifishell --gcc-tools-base ~/zephyr-sdk-1.0.1/gnu/riscv64-zephyr-elf/bin/riscv64-zephyr-elf- --report-max-static-stack-usage log_process_thread_func ~/zephyr/build/shell/zephyr/zephyr.elf
While trying to apply puncover to the wifi shell sample from zephyr RTOS[1] on a ESP32C3 (the RISCV one) I ran into a bug:
Debugging the issue I found it came from one of the function call instructions.
This is caused, by the more complicated call scheme around this instruction.
There is
jalandjalr, the later can be relative to a register, which is the issue.So the call address cannot be parsed from one single instruction. Cite:
So the culprit assembly line is
4038a354:\t004080e7 jalr 4(ra) # 40000354 <memset>.Applying the
enhance_call_tree_patternusually takes group 3 of the regex as the call address.But in this case group 3 is 4, but which means register ra offset by 4.
So correct call path deduction from assembly is much harder on RISCV to implement and remains with...
Exporting the json report and grepping through it
cat wifishell.json | grep -e 'jal\\' | wc -l(7181 instances) andcat wifishell.json | grep -e 'jalr' | wc -l(4429 instances) shows both instruction are used - depending how far the function are apart.GCC output of some includes the callee name, sometimes only the register.
More examples
cat wifishell.json | grep jalr:[1] build by
west build --sysbuild -p -b xiao_esp32c3/esp32c3 samples/net/wifi/shell/ -- -DCONFIG_STACK_USAGE=ythen
puncover --generate-report --report-tag 123 --report-type json --build_dir ~/zephyrproject/zephyr/build/ --elf_file ~/zephyr/build/shell/zephyr/zephyr.elf --report-filename wifishell --gcc-tools-base ~/zephyr-sdk-1.0.1/gnu/riscv64-zephyr-elf/bin/riscv64-zephyr-elf- --report-max-static-stack-usage log_process_thread_func ~/zephyr/build/shell/zephyr/zephyr.elf