microPython的源码解析之 profile.c

本文深入解析MicroPython的源码组件`profile.c`,探讨其在微控制器和嵌入式环境中的作用。内容包括函数解读、执行逻辑流程,帮助读者理解MicroPython的代码执行过程和调试技巧。涉及的函数包括字节码解析、帧对象信息获取、跟踪回调等功能,配合流程图展示执行逻辑。
摘要由CSDN通过智能技术生成

MicroPython 是一种适用于微控制器和其他受限环境的 Python 编程语言的实现。它旨在提供与 Python 3 语言的紧密兼容,同时考虑到内存和计算资源的限制。MicroPython 库是这门语言的核心组成部分,提供了一系列的模块和函数,使得开发者能够在硬件上执行各种任务。
下面将通过系列文章,逐一解读microPython,以便让读者了解掌握microPython的整个核心逻辑.,以便读者可以透过这个Python的最小内核,掌握Python解析器的核心实现逻辑,学习世界上最优秀的源码设计之一.


microPython Python最小内核源码解析
NI-motion运动控制c语言示例代码解析
python编程示例系列 python编程示例系列二
python的Web神器Streamlit


这段代码定义了一个函数mp_prof_print_instr,它用于打印微Python字节码指令的详细信息。函数首先通过mp_prof_opcode_decode解码当前指令,然后根据解码结果和前导信息(包括源文件名、代码块名称等)打印出详细的指令信息。这些信息包括指令的地址、操作码、操作数、参数对象等。通过这些信息,开发者可以更好地理解和调试微Python代码的执行过程。

#include "py/profile.h" // 包含 profiling 相关的头文件
#include "py/bc0.h" // 包含字节码相关的头文件
#include "py/gc.h" // 包含垃圾回收机制的头文件
#include "py/objfun.h" // 包含对象和函数相关的头文件

#if MICROPY_PY_SYS_SETTRACE // 如果启用了系统跟踪功能

#if !MICROPY_PERSISTENT_CODE_SAVE // 检查是否启用了代码持久化保存功能
// settrace 功能要求我们维护额外的元数据在原始的代码对象上,这通常只在写入 .mpy 文件时进行。
#error "MICROPY_PY_SYS_SETTRACE requires MICROPY_PERSISTENT_CODE_SAVE to be enabled" // 如果没有启用,将报错提示需要启用该功能
#endif

#define prof_trace_cb MP_STATE_THREAD(prof_trace_callback) // 定义宏,用于获取当前线程的 profiling 回调函数
#define QSTR_MAP(context, idx) (context->constants.qstr_table[idx]) // 定义宏,用于获取特定上下文中的字符串

// 根据字节码获取对应的源代码行号
static uint mp_prof_bytecode_lineno(const mp_raw_code_t *rc, size_t bc) {
   
    const mp_bytecode_prelude_t *prelude = &rc->prelude; // 获取代码的前奏部分
    return mp_bytecode_get_source_line(prelude->line_info, prelude->line_info_top, bc); // 根据行信息和行信息顶部,获取字节码对应的行号
}

// 从字节码中提取前奏部分
void mp_prof_extract_prelude(const byte *bytecode, mp_bytecode_prelude_t *prelude) {
   
    const byte *ip = bytecode; // 指向当前字节码的指针

    // 解析前奏部分的签名
    MP_BC_PRELUDE_SIG_DECODE(ip);
    prelude->n_state = n_state; // 设置状态数量
    prelude->n_exc_stack = n_exc_stack; // 设置异常栈数量
    prelude->scope_flags = scope_flags; // 设置作用域标志
    prelude->n_pos_args = n_pos_args; // 设置位置参数数量
    prelude->n_kwonly_args = n_kwonly_args; // 设置关键字参数数量
    prelude->n_def_pos_args = n_def_pos_args; // 设置默认位置参数数量

    // 解析前奏部分的大小
    MP_BC_PRELUDE_SIZE_DECODE(ip);

    prelude->line_info_top = ip + n_info; // 设置行信息顶部的位置
    prelude->opcodes = ip + n_info + n_cell; // 设置操作码的位置

    // 解析并设置块名称索引
    prelude->qstr_block_name_idx = mp_decode_uint_value(ip);
    for (size_t i = 0; i < 1 + n_pos_args + n_kwonly_args; ++i) {
   
        ip = mp_decode_uint_skip(ip); // 跳过参数的解码
    }
    prelude->line_info = ip; // 设置行信息的开始位置
}

// 打印 code 对象
static void code_print(const mp_print_t *print, mp_obj_t o_in, mp_print_kind_t kind) {
   
    (void)kind; // 忽略打印类型
    mp_obj_code_t *o = MP_OBJ_TO_PTR(o_in); // 将输入的对象转换为 code 对象
    const mp_raw_code_t *rc = o->rc; // 获取原始代码对象
    const mp_bytecode_prelude_t *prelude = &rc->prelude; // 获取前奏部分
    mp_printf(print, // 使用打印函数输出信息
        "<code object %q at 0x%p, file \"%q\", line %d>",
        QSTR_MAP(o->context, prelude->qstr_block_name_idx), // 打印块名称
        o, // 打印对象地址
        QSTR_MAP(o->context, 0), // 打印文件名
        rc->line_of_definition // 打印定义行号
    );
}

// 获取 code 对象的常量
static mp_obj_tuple_t *code_consts(const mp_module_context_t *context, const mp_raw_code_t *rc) {
   
    // 创建一个新的元组对象,用于存储常量
    mp_obj_tuple_t *consts = MP_OBJ_TO_PTR(mp_obj_new_tuple(rc->n_children + 1, NULL));

    size_t const_no = 0; // 常量的索引
    for (size_t i = 0; i < rc->n_children; ++i) {
    // 遍历所有子对象
        mp_obj_t code = mp_obj_new_code(context, rc->children[i]); // 创建一个新的 code 对象
        if (code == MP_OBJ_NULL) {
   
            m_malloc_fail(sizeof(mp_obj_code_t)); // 如果创建失败,报告内存分配失败
        }
        consts->items[const_no++] = code; // 将新创建的 code 对象添加到元组中
    }
    consts->items[const_no++] = mp_const_none; // 添加一个 None 对象作为占位符

    return consts; // 返回创建的元组对象
}

// 获取原始代码的 lnotab 信息
static mp_obj_t raw_code_lnotab(const mp_raw_code_t *rc) {
   
    // 获取原始代码的前奏部分
    const mp_bytecode_prelude_t *prelude = &rc->prelude;
    uint start = 0; // 开始位置
    uint stop = rc->fun_data_len - start; // 结束位置

    uint last_lineno = mp_prof_bytecode_lineno(rc, start); // 上一个行号
    uint lasti = 0; // 上一个指令位置

    // 定义一个缓冲区的大小,用于存储行号差异和指令差异
    const uint buffer_chunk_size = (stop - start) >> 2; // 经验值
    uint buffer_size = buffer_chunk_size;
    byte *buffer = m_new(byte, buffer_size); // 创建缓冲区
    uint buffer_index = 0; // 缓冲区的当前位置

    // 遍历原始代码,计算行号和指令的差异
    for (uint i = start; i < stop; ++i) {
   
        uint lineno = mp_prof_bytecode_lineno(rc, i); // 当前行号
        size_t line_diff = lineno - last_lineno; // 行号差异
        if (line_diff > 0) {
   
            uint instr_diff = (i - start) - lasti; // 指令差异

            // 确保差异值不会超过 255
            assert(instr_diff < 256);
            assert(line_diff < 256);

            // 如果缓冲区空间不足,扩展缓冲区
            if (buffer_index + 2 > buffer_size) {
   
                buffer = m_renew(byte, buffer, buffer_size, buffer_size + buffer_chunk_size);
                buffer_size = buffer_size + buffer_chunk_size;
            }
            last_lineno = lineno; // 更新上一个行号
            lasti = i - start; // 更新上一个指令位置
            buffer[buffer_index++] = instr_diff; // 写入指令差异
            buffer[buffer_index++] = line_diff; // 写入行号差异
        }
    }

    // 创建一个新的字节对象,表示 lnotab 信息
    mp_obj_t o = mp_obj_new_bytes(buffer, buffer_index
  • 22
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

openwin_top

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值