我们计算编写这些函数异常简单的版本,但真正的调试器有 thread plan 的概念,它封装了所有的单步信息。例如,调试器可能有一些复杂的逻辑去决定断点的地位,然后有一些回调函数用于断定单步操作是否完成。这个中有异常多的基本举措措施,我们只采取一种朴实的办法。我们可能会心外埠跳过断点,但如不雅你愿意的话,你可以花一些时光把所有的细节都处理好。
系列文┞仿索引
跟着后面文┞仿的宣布,这些链接会逐渐生效。
- 预备情况
- 断点
- 存放器和内存
- Elves 和 dwarves
- 源码和旌旗灯号
- 源码级慢慢履行
- 源码级断点
- 调用栈展开
- 攫取变量
- 下一步
揭秘指令级慢慢履行
在前几篇博文中我们进修了 DWARF 信息以及它若何使我们将机械码和上层源码接洽起来。这一次我们经由过程为我们的调试器添加源码级慢慢调试将该常识应用于实际。
我们正在超出了自我。起首让我们经由过程用户接口揭秘指令级单步履行。我决定将它切分为能被其它部分代码应用的 single_step_instruction 和确保是否启用了某个断点的 single_step_instruction_with_breakpoint_check 两个函数。
- void debugger::single_step_instruction() {
- ptrace(PTRACE_SINGLESTEP, m_pid, nullptr, nullptr);
- wait_for_signal();
- }
- void debugger::single_step_instruction_with_breakpoint_check() {
- //起首,检查我们是否须要停用或者启用某个断点
- if (m_breakpoints.count(get_pc())) {
- step_over_breakpoint();
- }
- else {
- single_step_instruction();
- }
- }
正如以往,另一个敕令被集成到我们的 handle_command 函数:
- else if(is_prefix(command, "stepi")) {
- single_step_instruction_with_breakpoint_check();
- auto line_entry = get_line_entry_from_pc(get_pc());
- print_source(line_entry->file->path, line_entry->line);
- }
应用新增的┞封些函数我们可以开端实现我们的源码级慢慢履行函数。
实现慢慢履行
对于彪炳 step_out,我们只是在函数的返回地址处设一个断点然后持续履行。我临时还不想推敲调用栈展开的细节 - 这些都邑在后面的部分介绍 - 但可以说返回地址就保存在栈帧开端的后 8 个字节中。是以我们会攫取栈指针然后在内存相对应的地址攫取值:
- void debugger::step_out() {
- auto frame_pointer = get_register_value(m_pid, reg::rbp);
- auto return_address = read_memory(frame_pointer+8);
- bool should_remove_breakpoint = false;
- if (!m_breakpoints.count(return_address)) {
- set_breakpoint_at_address(return_address);
- should_remove_breakpoint = true;
- }
- continue_execution();
- if (should_remove_breakpoint) {
- remove_breakpoint(return_address);
推荐阅读
run(getData) 在 JavaScript 的世比赛,所有代码都是单线程履行的,因为这个“缺点”,导致 JavaScript 的所有收集操作,浏览器事宜,都必须是异步履行。异步操作会在将来的某个>>>详细阅读
地址:http://www.17bianji.com/lsqh/36973.html
1/2 1