For a University assignment for a course called Real-Time Systems we had to implement a prototype for timing analysis by tracing instructions.
The idea is that when executing a program, ptrace controls the execution and stops after each instruction. After this the actual instruction in the memory can be fetched. Knowing which instruction is executed you could associate this with a certain time, add all times together for each instruction and you could dynamically determine how long a program would run: profit!
I made a prototype that can be viewed on GitHub.
ptrace is a tool that can control processes. It is usually used in debuggers, to trace calls, stop at breakpoints, change variables and such.
PTRACE_SINGLESTEP you can also stop at after each instruction. If you
like to know how many instructions are executed when executing your program
you can simply add a counter.
It's not only possible to step trough the instructions, once the program is stopped at some instruction, you can read the registers, including the instruction pointer.
The registers are read with
ptrace(PTRACE_GETREGS, pid, NULL, ®s). The
regs variable is a
user_regs_struct. In this struct the
the instruction pointer (for 64 bit, for 32 bit it's called
Once you know the address of the instruction, why not read the actual data
at that address? Sure, why not! That is exactly what
Read a word at the address addr in the tracee's memory, returning the word as the result of the ptrace() call.
Unfortunately the result, something like
c3c74880cd, didn't say me very
much. Neither did I want to read through the entire Intel documentation to
figure out what it could mean. I needed something like
objdump which could
disassemble a compiled binary…
After searching the internet and GitHub I found udis86. Udis86 is a disassembler Library for x86 and x86-64. With the help of some examples over the internet I ended up with something like:
With the code above the instructions are printed, which is pretty nice. If we compile and disassemble the following assembly:
We get the following output already:
Our final goal however was to know the execution time of the program.
ud_t struct we find that it has a
This is the perfect candidate to be used in a simple
case statement, because
all possible values are constants like
For the hello world assembly code, which has only
the following code is already enough:
Of course the values are just made up, because you'd have to correctly measure or look into the documentation how long the instruction would take.
I think this quite cool that it's possible. It is a combination of dynamic and static execution time analysis. If you want to know the Worst Case Execution Time (WCET), static analysis is always more than the actual WCET and dynamic is always less. This combination would thus maybe be more or less the correct value.
Unfortunately it's probably not as straightforward with modern hardware to exactly determine the execution time. Modern hardware has many features (e.g. piping and caching) to improve the Average Execution Time. This is what 99% of the users want. In case you're doing such an analysis you are probably not only interested in the average, but more in the boundaries: the worst or best cases.
If the program contains enough instructions, it's probably possible to say something but you can't prove it. When you're building a hard real-time system that might be necessary though.