我写信是为了看看你们中是否有人见过或听说过我将要描述的想法的实现。
我有兴趣为嵌入式目标开发一个printf样式的调试库。 目标是非常远程的,我和目标之间的通信带宽预算非常紧张,所以我希望能够以非常有效的格式获得调试消息。
通常,调试语句类似于以下内容:
myDebugLibraryPrintf("Inside loop, processing item %d out of %d.n", i, numItems);
当然,当它被扩展为文本时,打印的字符串类似于“内部循环,处理项目5中的10个 n”,总共约42个字节左右。 此语句打印出的90%以上的数据是静态的,字面的 – 在编译时已知。 当然,在编译时只知道“5”和“10”。
我想做的是只能回发那两个整数(8个字节而不是42个)。 一旦我收到了这些数据,我就会有一些“解码器响铃”让我“重新构建”收到的数据并在我的位置打印出完整的调试信息。
我会自动生成“解码器环”(作为构建过程的一部分),在编译时为每个myDebugLibraryPrintf()语句提供一个唯一的ID,并生成一个将这些唯一ID映射到原始格式字符串的表。 然后,只要在目标上调用myDebugLibraryPrintf(),它就会传输唯一ID以及格式字符串中看到的任何"%d"
, "%f"
等varargs值,但格式字符串本身不会被传输。 (我现在可能只是禁止"%s"
项目…)回到我的位置,我们将有一个程序查找表中的唯一ID,找到相应的格式字符串,并用它来重建原始调试消息。
我觉得以前有人可能有过这个想法,我想社区中的某个人可能会看到类似的东西(或者甚至知道这样做的开源库)。
约束:
谢谢!
我只看到这个想法用一组预先定义的字符串实现。 代码看起来像debug_print(INSIDE_LOOP_MSG_ID, i, n)
。 当开发人员想要添加新消息时,他们必须将新文本放在特定的头文件中并为其提供新的ID。
我认为从正常的印刷语句中即时生成它的想法是一个有趣的挑战。 我没有遇到任何现有的实现。
一个想法可能是宏/模板,它在编译时将第一个字符串参数转换为哈希值 。 因此开发人员编写了debug_print("test %d",i)
,它被编译为debug_port_send(0x1d3s, i)
。 编写后处理脚本以提取字符串和哈希以便在接收方使用应该很简单。 (解决哈希冲突的最简单方法是提供错误消息并强制用户稍微改变措辞)。
编辑:
所以我在上面的链接中使用编译时哈希尝试了这个。
#define QQuot_(x) #x #define QQuote(x) QQuot_(x) #define Debug_Print(s, v) (Send( CONSTHASH(QQuote(__LINE__)##s), *((long*)&(v)))) void Send(long hash, long value) { printf("Sending %x %xn", hash, value); //replace with COMMS } int main() { int i = 1; float f= 3.14f; Debug_Print("This is a test %d", i); i++; Debug_Print("This is a test %d", i); Debug_Print("This was test %f", f); }
有了更多的聪明,你可以支持多个论点。 检查dissasembly显示所有哈希确实在编译时计算。 输出是预期的,没有来自相同字符串的冲突。 ( 此页面确认hex对于3.14是正确的):
Sending 94b7555c 1 Sending 62fce13e 2 Sending 506e9a0c 4048f5c3
您现在需要的只是一个文本处理脚本,可以在代码上运行,该代码从Debug_Print中提取字符串,计算哈希值并填充接收方的表格。 接收方从Send
调用获取哈希值,查找与其一起使用的字符串,并将其与参数一起传递给正常的printf调用。
我看到的唯一问题是编译时哈希中的嵌套宏令我的重构插件混乱并导致我的IDE响应性崩溃 。 禁用加载项会删除该问题。
我见过在ARM平台上完成类似工作的东西。 我相信它被称为“ 嵌入式跟踪宏单元 ”。 一系列宏转换语句,如TRACE_POWER_SYSTEM_VOLTAGE_REGULATOR_TRIGGER(inputX);
将两个寄存器写入ETM寄存器。 请注意,这只能接受16位,32位和64位整数作为参数。
我们可以使用ARM工具提取这些(带时间戳)缓冲区。 然后我们应用一个预先编译的技巧来将第一个(索引)寄存器写入转换为如下所示的输出文件:
timestamp | POWER SYSTEM | VOLTAGE REGULATOR TRIGGER | 0x2380FF23
已经检查了代码以确定参数的数据类型,因此我们不必费心。 它还可以使用“实时”时间戳(而不是自上电后的ms)以及跟踪语句的文件和行号进行注释。
ARM设置为在内部(并且非常快速)存储此循环缓冲区,因此可以在生产中使用它。 即使你没有硬件支持,但……这方面的某些方面可以轻松复制。
请注意,在分析跟踪时,您只使用与设备上运行的代码的特定版本匹配的“解码”文件,这一点非常重要。
我似乎记得很多工具用于提取字符串文字以实现国际化。 GNU字符串可以直接从可执行文件中提取字符串。 这应该有助于完成部分任务。
我有同样的问题,我想减少图像大小(由于微小的嵌入式闪存)。 我的解决方案是发送文件名和行(应该是14-20字节)并在服务器端有一个源解析器,它将生成实际文本的映射。 这样,实际代码将不包含“格式”字符串,但每个文件只包含一个“filename”字符串。 此外,文件名可以很容易地用枚举替换(与替换代码中的每个字符串不同)以减少COMM吞吐量。
我希望样本psaudo代码有助于澄清这个想法:
/* target code */ #define PRINT(format,...) send(__FILE__,__LINE__,__VA_ARGS__) ... /* host code (c++) */ void PrintComm(istream& in) { string fileName; int line,nParams; int* params; in>>fileName>>line>>nParams; if (nParams>0) { params = new int[nParams]; for (int i=0; i>params[i]; } const char* format = FindFormat(fileName,line); ... delete[] params; }
以上就是c/c++开发分享printf()使用字符串表“解码器环”调试库相关内容,想了解更多C/C++开发(异常处理)及C/C++游戏开发关注计算机技术网(www.ctvol.com)!)。
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/c-cdevelopment/562739.html