【问题标题】:C struct introspection at runtime运行时的 C 结构自省
【发布时间】:2015-03-08 07:40:18
【问题描述】:

C 语言是否有允许运行时结构自省的工具?

上下文是这样的: 我有一个响应外部事件的守护进程,并且对于每个事件,我们都携带一个执行上下文结构(“上下文”)。上下文大而杂乱,包含对各种状态的引用。

处理完事件后,我希望能够通过过滤器运行上下文,如果它符合某些条件,则删除一条日志消息以帮助调试。但是,由于我希望将其用于现场调试,因此直到运行时我才知道过滤哪些标准是有用的。

我的理想解决方案本质上是允许用户编写 C 风格的布尔表达式并让程序使用它。比如:

activate_filter context.response_time > 4.2 && context.event.event_type == foo_event

到目前为止,被折腾的想法包括:

  • 提供一组我们知道如何访问的有限字段。
  • 将所有相关结构封装在某种宏中,在运行时生成自省工具。
  • 编写一个知道(版本化的)头文件所在位置的 python 脚本,生成 C 代码并将其编译为 dll,然后守护程序将其加载并用作过滤器。显然,这种方法有一些额外的安全考虑。

在我开始疯狂的设计追逐之前,有没有人知道这种事情的例子?我做了一些谷歌搜索,但还没有想出太多。

【问题讨论】:

标签: c introspection


【解决方案1】:

我从SWIG-CSV parser 中生成了自省。

假设 C 代码包含如下结构,

class Bike {
public:
    int color;      // color of the bike
    int gearCount;      // number of configurable gear
    Bike() {
        // bla bla
    }
    ~Bike() {
        // bla bla
    }
    void operate() {
        // bla bla
    }
};

然后会生成如下CSV元数据,

Bike|color|int|variable|public|
Bike|gearCount|int|variable|public|
Bike|operate|void|function|public|f().

如果需要,现在可以很容易地使用 python 或 C/C++ 解析 CSV 文件。

import csv
with open('bike.csv', 'rb') as csvfile:
    bike_metadata = csv.reader(csvfile, delimiter='|')
    # do your thing

【讨论】:

    【解决方案2】:

    我还建议从另一个角度解决这个问题。您问题中的关键词是:

    上下文又大又乱

    这就是问题所在。一旦你把它清理干净,你可能会想出一个干净的日志工具。

    考虑以某种简单、灵活的格式重新定义上下文struct 中的所有字段,例如XML。一个简单的 `XML 模式,它列出了结构的所有成员、它们的类型,可能还有其他一些元数据,甚至是记录该字段的注释。

    然后,将读取 XML 文件并生成代码实际使用的可编译 C 结构的快速而肮脏的样式表放在一起。然后,一个不同的样式表生成机器人生成的代码,枚举结构中的每个字段,并生成将每个字段转换为字符串的代码。

    因此,使用用户提供的过滤字符串来连接某种日志记录工具变得更容易。您必须想出某种方法来解析任意过滤字符串。了解lexyacc 会派上用场。

    这种性质的事情以前也做过。

    XCB library 是 X11 协议的 C 客户端库。该协议定义了各种类型的二进制消息,这些消息本质上是简单的structs,客户端和服务器通过套接字相互传递。 libxcb 的实现方式是,所有 X11 消息和其中的所有数据类型都在 XML 定义中描述,样式表自动生成 C 结构定义,以及解析它们的代码,并提供相当干净的 C API解析并生成 X11 消息。

    【讨论】:

      【解决方案3】:

      你可能从错误的角度来处理这个问题。

      日志记录通常用于方便调试。该程序将各种事件写入日志文件。为了提取感兴趣的条目,过滤应用于日志文件。

      有时程序会产生过多的事件;日志库通常通过提供详细程度控制来解决这个问题。基本上,日志记录函数需要一个额外的参数来告诉当前消息的详细程度。如果该值高于全局配置的阈值,则消息将被丢弃。一些库甚至允许在每个模块的基础上控制详细级别(例如:google log)。

      另一种可能的方法是利用调试器的强大功能,因为调试器可以访问各种元信息。可以在任意条件范围内创建条件断点测试变量。一旦程序停止,任何信息都可以从范围中提取。这可以使用调试器提供的脚本工具自动化(gdb 有很好的工具)。

      最后还有一些工具可以生成胶水代码来使用来自脚本语言的 C 库。一个例子是 SWIG。它分析头文件并生成允许脚本语言调用函数、访问结构字段等的代码。

      您的过滤器表达式将成为 Lua 中的程序(也支持其他脚本语言)。您调用这个程序,传递指向执行上下文结构(“上下文”)的指针。感谢 SWIG Lua 程序生成的访问器可以检查结构中的任何字段。

      【讨论】:

      • 好多了,一个学习工具而不是学习lex和yacc和xml解析等。
      • 感谢您的回复!没错,这是日志记录的非典型用途。不幸的是,这个程序每秒处理(数万)个事件,并且记录所有事件已被证明对性能有很大影响。我已经对基本过滤器进行了一些实验,但它们似乎没有同样的减速。我怀疑在调试器中运行会产生类似的性能影响,而且我们希望在客户站点的实时服务器上使用这个工具。不过,我肯定会去看看 SWIG。
      猜你喜欢
      • 2018-09-30
      • 1970-01-01
      • 1970-01-01
      • 2014-05-08
      • 1970-01-01
      • 2021-12-02
      • 2019-04-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多