libmcpp
更新时间: 2026/06/04
在Gitcode上查看源码

1. 组件概述

1.1 组件简介

libmcpp 是一个现代 C++ 开发框架,专为 openUBMC 项目设计,旨在提供高效、安全、易用的 C++ 开发环境。该框架采用五层分层模块化设计,遵循现代 C++17 最佳实践,主要面向嵌入式系统(尤其是 BMC 固件)和服务器应用程序开发。框架提供了丰富的基础设施和功能组件,涵盖反射系统、异常处理、日志系统、应用框架、变体类型、信号槽、Future/Promise 异步编程、D-Bus 通信、共享内存数据库等核心能力,使开发人员能够快速构建可靠的应用程序。

libmcpp 采用 Mulan PSL v2 开源许可证,支持 GCC 7+、Clang 5+、MSVC 2019+ 等主流编译器,可在 Linux、macOS、Windows、FreeBSD 等多种平台上运行。

1.2 解决什么问题

传统 BMC 开发面临以下痛点:

  • 代码复用性差:缺乏统一的开发框架,各模块重复实现基础功能,导致开发效率低下
  • 模块耦合度高:模块间直接依赖,难以独立开发、测试和维护,无法实现功能热插拔
  • 内存管理复杂:手动内存管理容易出错,内存泄漏和悬垂指针问题频发,影响系统稳定性
  • 配置管理混乱:缺乏统一的配置管理机制,各模块各自解析配置,格式不统一
  • 调试和维护困难:缺乏统一的日志和诊断机制,问题定位耗时长
  • 类型安全问题:传统 C 风格接口缺乏编译期类型检查,运行时错误难以防范

libmcpp 通过以下方式解决上述问题:

  • 统一开发框架:提供插件化架构(Plugin + Service + Supervisor),通过 application 单例协调各管理器工作
  • 现代 C++ 安全保障:采用 RAII、智能指针、编译期类型检查等现代 C++ 技术确保内存和类型安全
  • 声明式配置管理:基于 JSON 的统一配置管理系统,支持命令行参数解析和配置文件加载
  • 结构化日志系统:提供多级日志、多目标输出、结构化日志格式,便于日志检索和问题定位
  • 反射系统:提供运行时类型信息获取和序列化/反序列化能力,简化数据交换

1.3 核心功能

libmcpp 框架采用五层分层架构设计,从底层到顶层依次为:

层次模块功能说明
基础设施层common, exception, filesystem, string, time, error, sync, memory通用工具、异常处理、文件系统、字符串处理、时间工具、错误码/错误域、并发同步、智能指针
数据处理层variant, dict, json, reflect, expr动态类型系统、键值对容器、JSON 解析/生成、运行时反射、表达式引擎
功能服务层db, log, interprocess共享内存对象数据库、结构化日志系统、进程间通信(共享内存、互斥锁等)
应用程序框架层core (plugin_manager, service_factory, service_manager, config_manager, supervisor_manager)插件管理、服务注册/创建、配置管理、监督树生命周期管理
应用层discovery_service, micro_component自发现服务、MicroComponent 微组件标准接口

核心特性一览

  • 反射系统:运行时类型信息获取与序列化,支持嵌套类型和 STL 容器,通过 MC_REFLECT 宏注册元数据
  • 异常处理:带上下文追踪的统一异常体系,支持异常链(exception chain)、错误码映射和堆栈符号解析
  • 日志系统:多后端异步日志,支持 ConsoleAppender、FileAppender、RotatingFileAppender 等,提供结构化日志格式 ${key}
  • 应用程序框架:插件+服务架构,支持插件动态加载(dlopen)、服务工厂模式、监督树(Supervisor Tree)故障恢复
  • 变体类型(variant):类似于 std::any 的动态类型,支持序列化和与 JSON 无缝互操作
  • D-Bus 通信:提供 C++ 和 Lua 双层接口,支持同步/异步通信、信号订阅、对象路径注册
  • Future/Promise:异步编程模型,支持链式操作和异常传播
  • MicroComponent 接口:标准化的 BMC 微组件接口,包含健康检查、配置管理、调试、日志限流、性能统计等

1.4 关键术语表

术语解释
Plugin(插件)可动态加载的功能模块,通过 dlopen 加载 .so 文件,向框架注册服务类型
Service(服务)应用程序的核心功能单元,有明确的生命周期(init → start → run → stop → cleanup)
Supervisor(监督器)负责监督服务生命周期、检测故障并执行恢复策略(借鉴 Erlang/OTP 监督树模式)
DDS(Device Description Schema)定义部件的设备描述规范,包括接口定义、属性声明和方法签名
CSR(Component Self-Description Record)存储部件的自描述记录,包含配置数据、运行时状态和元数据信息
MicroComponent(微组件)openUBMC 的核心架构单元,每个 BMC 组件都继承自 micro_component_object
variant动态类型容器,类似 std::any,支持类型安全的访问和序列化
dict键值对容器,保持插入顺序,支持嵌套和共享数据模型(copy-on-write)
RAIIResource Acquisition Is Initialization,资源获取即初始化,确保资源自动管理
MC_REFLECT反射注册宏,用于向反射系统注册类的成员和方法元数据
MC_INTERFACE接口元数据声明宏,用于声明 D-Bus 接口信息
MC_OBJECT对象元数据声明宏,定义业务对象的类名、DBus 路径模板和接口列表

1.5 外部交互边界图

2. API 使用说明与示例

2.1 反射系统

功能说明

反射系统提供运行时类型信息获取和对象序列化/反序列化能力。通过 MC_REFLECT 宏注册类的成员元数据后,即可在运行时动态访问成员、进行序列化操作,支持嵌套类型和 STL 容器。

参数说明

属性参数说明

反射系统通过 MC_REFLECT(ClassName, (member1)(member2)...) 宏注册类的可反射成员,每个成员会被赋予对应的名称用于运行时访问和序列化。

方法参数说明
方法名入参类型出参类型描述取值范围
MC_REFLECT(Class, ...)类名, 成员列表void注册类的反射元数据宏定义,编译时展开
MC_INTERFACE(Name, Version)接口名称, 版本号void声明接口元数据字符串常量
MC_OBJECT(Class, ClassName, PathTmpl, Interfaces)类, 类名, 路径模板, 接口列表void注册业务对象元数据编译时常量

返回值与异常

返回值含义触发条件处理建议
反射操作成功成员访问正确成员名匹配、类型正确
std::bad_variant_access类型访问错误variant 类型不匹配检查 .as<T>() 的类型参数

应用场景

  • 对象序列化为 JSON 字符串进行持久化存储或网络传输
  • 从 JSON 字符串反序列化为 C++ 对象
  • 运行时动态访问对象成员
  • 与 D-Bus 属性系统集成,自动处理属性变更信号

限制条件

  • 需要 C++17 或更高版本编译器支持
  • 反射注册必须在编译期完成,不支持运行时动态添加新成员类型
  • 反射信息存储为静态数据,增大可执行文件体积

调试示例

代码调试
cpp
#include <mc/reflect.h>

class User {
public:
    std::string name;
    int id;
    std::vector<std::string> tags;
};
MC_REFLECT(User, (name)(id)(tags))

// 序列化对象
User user{"admin", 1001, {"staff", "devops"}};
mc::variant var = user;
std::cout << "JSON: " << mc::json::to_string(var);

// 反序列化对象
std::string json = "{\"name\":\"guest\",\"id\":1002,\"tags\":[\"visitor\"]}";
User new_user = mc::json::from_string(json).as<User>();

2.2 日志系统

功能说明

日志系统提供灵活的多级日志记录和输出功能,采用模块化设计,支持多种日志级别(trace/debug/info/notice/warn/error/fatal)、多种输出目标(控制台、文件、滚动文件、按日期轮转)和结构化日志格式。

参数说明

属性参数说明

日志级别定义:

级别数值说明
trace0最详细的跟踪信息
debug1调试信息
info2一般运行信息
notice3重要节点通知
warn4警告信息
error5错误信息
fatal6致命错误
方法参数说明
方法名入参类型出参类型描述取值范围
MC_LOG_REGISTER_APPENDER名称, Appender类型void注册日志输出目标宏定义
ilog / wlog / elog / dlog / nlog / tlog / flogformat, args...void全局日志宏(需引入 mc/log.h)结构化格式 ${key}
mc_ilog / mc_wlog / mc_eloglogger, format, args...void基于指定 Logger 的日志宏同上

返回值与异常

返回值含义触发条件处理建议
日志写入成功日志已输出到目标日志级别满足过滤条件
日志被丢弃日志级别低于当前设置日志级别过滤调整日志级别或使用更高级别宏

应用场景

  • 系统运行状态记录和审计
  • 故障排查和问题定位
  • 性能监控和分析
  • 运维诊断

限制条件

  • 在生产环境中建议使用 info 或以上级别,避免大量 debug 日志影响性能
  • 异步日志模式下,程序异常退出可能导致少量日志丢失
  • 日志文件需要定期清理或轮转,防止磁盘占满

调试示例

代码调试
cpp
#include <mc/log.h>

// 注册日志后端
MC_LOG_REGISTER_APPENDER(console, mc::log::ConsoleAppender);

int main() {
    // 配置日志系统
    mc::log::Logger::getInstance()
        .setAppender<mc::log::RollingFileAppender>("app.log", 10*1024*1024, 5)
        .setLevel(mc::log::Level::Debug);

    // 记录简单日志
    MC_LOG_INFO("系统初始化完成");

    // 记录结构化日志
    MC_LOG_INFO("用户活动")
        << "user_id=" << 42
        << "action=" << "login";

    // 使用全局日志宏
    ilog("系统初始化完成");
    ilog("用户 ${user} 从 ${ip} 登录成功", ("user", "admin")("ip", "192.168.1.100"));
    elog("连接数据库失败: ${error}", ("error", "Connection timeout"));

    return 0;
}

2.3 应用程序框架

功能说明

应用程序框架是 libmcpp 的核心模块,采用插件 + 服务的架构模式。通过 application 单例协调 PluginManager、ServiceFactory、ServiceManager、ConfigManager 和 SupervisorManager 的工作,实现模块化应用程序开发。

参数说明

属性参数说明

应用程序框架的基本组成部分:

管理器主要职责
ConfigManager命令行参数解析、配置文件加载、配置数据提供
PluginManager动态加载/卸载插件,管理插件依赖和生命周期
ServiceFactory服务类型注册和创建
ServiceManager管理服务实例的生命周期(创建、启动、停止)
SupervisorManager监督器的创建和管理,服务健康监控和故障恢复
方法参数说明
方法名入参类型出参类型描述取值范围
application::instance()voidapplication&获取应用程序单例全局唯一实例
application::initialize(argc, argv)int, char**bool初始化应用程序成功返回 true
application::start()voidapplication&启动应用程序链式调用
application::exec()voidvoid进入事件循环阻塞直到 stop()
application::stop()voidvoid停止应用程序安全退出
register_plugin<T>()插件类型bool注册插件模板参数

返回值与异常

返回值含义触发条件处理建议
true操作成功初始化/启动正常完成
false操作失败配置错误、插件加载失败、依赖缺失检查日志定位失败原因

应用场景

  • BMC 应用程序开发和部署
  • 需要插件化架构的服务端程序
  • 需要高可靠性的嵌入式系统(监督树模式故障恢复)

限制条件

  • 单个应用程序实例不支持超过 256 个插件同时加载
  • 插件动态卸载可能导致依赖该插件的服务暂时不可用
  • 需要 POSIX 线程库支持

调试示例

代码调试
cpp
#include <mc/core/application.h>

// 定义服务
class my_service : public mc::service_base<my_service> {
public:
    static const char* service_type() { return "example.my_service"; }

    static void register_options(mc::po::options_description& cfg_opts) {
        cfg_opts.add_options()
            ("my_service.timeout", mc::po::value<int>()->default_value(30), "超时时间(秒)");
    }

    static const std::vector<std::string>& dependencies() {
        static std::vector<std::string> deps = {"logger"};
        return deps;
    }

    bool init(mc::dict args) override {
        m_timeout = args.get("my_service.timeout").as<int>();
        return true;
    }

    bool start() override {
        set_state(mc::service_state::running);
        return true;
    }

    bool stop() override {
        set_state(mc::service_state::stopped);
        return true;
    }

private:
    int m_timeout;
};

// 定义插件
class my_plugin : public mc::plugin_base<my_plugin> {
public:
    static const char* plugin_name() { return "my_plugin"; }
    bool initialize() override { return true; }
    void startup() override {}
    void shutdown() override {}
};

// 应用程序入口
int main(int argc, char** argv) {
    try {
        mc::application& app = mc::application::instance();
        app.set_version("1.0.0");
        app.register_plugin<my_plugin>();
        app.initialize(argc, argv);
        app.startup();
        app.exec();
        return 0;
    } catch (const std::exception& e) {
        std::cerr << "错误: " << e.what() << std::endl;
        return 1;
    }
}

2.4 异常处理

功能说明

libmcpp 提供统一的异常处理机制,基于 C++ 标准异常进行扩展,支持异常链(exception chain)、错误码映射、堆栈符号解析、异常信息格式化等能力。通过 MC_THROWMC_RETHROW_EXCEPTION 等宏简化异常的抛出和重新抛出。

参数说明

方法参数说明
方法名入参类型出参类型描述取值范围
MC_THROW(ExceptionType, msg)异常类型, 消息void抛出指定类型异常宏定义
MC_RETHROW_EXCEPTION(msg)消息void捕获并重新抛出,添加上下文宏定义
e.what()voidconst char*获取异常描述重写 std::exception::what
e.code()voidint64_t获取异常错误码返回异常代码
e.to_string()levelstd::string获取异常信息的字符串表示level 为日志级别

返回值与异常

返回值含义触发条件处理建议
异常被捕获异常已处理catch 块匹配异常类型根据异常类型采取对应处理
异常未被捕获程序终止无匹配的 catch 块添加通用 catch 块兜底

应用场景

  • 数据库操作失败时的错误传播
  • 插件加载失败的错误报告
  • 第三方库异常的包装和转换
  • 异步操作中的异常传播

调试示例

代码调试
cpp
#include <mc/exception.h>

void database_operation() {
    try {
        throw std::runtime_error("连接失败");
    } catch (const std::exception& e) {
        MC_THROW(mc::db_error, "数据库操作失败")
            << mc::error_info("operation", "query")
            << mc::error_info("sql", "SELECT * FROM users")
            << mc::error_cause(e);
    }
}

int main() {
    try {
        database_operation();
    } catch (const mc::exception& e) {
        std::cerr << "错误: " << e.what() << std::endl;
        std::cerr << "错误码: " << e.code() << std::endl;
    }
    return 0;
}

2.5 插件系统

功能说明

插件系统负责动态加载和管理插件模块。支持两种模块类型:内建模块(编译时静态链接)和动态模块(运行时动态加载 .so 文件)。每个模块拥有独立的反射工厂,实现模块间的完全类型隔离。

参数说明

方法参数说明
方法名入参类型出参类型描述取值范围
load_plugin(name)stringbool加载指定插件插件名称
load_plugins(names)vector<string>bool加载插件列表插件名称列表
unload_plugin(name)stringbool卸载指定插件已加载的插件名
unload_all_plugins()voidvoid卸载所有插件
find_plugin(name)stringplugin*查找已加载的插件插件名称
get_loaded_plugins()voidvector<string>获取已加载插件列表

返回值与异常

返回值含义触发条件处理建议
true加载成功插件 .so 文件有效、依赖满足
false加载失败插件文件不存在、版本不兼容、依赖缺失查看日志定位具体原因

应用场景

  • 热更新:不重启应用更新插件
  • 动态功能切换:根据运行条件动态加载/卸载功能模块
  • 故障隔离:卸载故障插件而不影响整个系统
  • 资源优化:卸载暂时不需要的插件释放内存

限制条件

  • 插件开发必须提供 create_plugindestroy_plugin 导出函数
  • 动态模块使用 dlopenRTLD_LOCAL 实现符号隔离
  • 卸载插件时需要考虑依赖关系和正在运行的服务实例

调试示例

代码调试
cpp
#include <mc/module.h>

// 内建模块定义
MC_MODULE(mc_devices)

namespace mc::devices {
class sensor {
public:
    void set_name(const std::string& name) { m_name = name; }
    const std::string& get_name() const { return m_name; }
private:
    std::string m_name{"default_sensor"};
};
}

MC_MODULE_REFLECT(mc_devices,
    (mc::devices::sensor, "Sensor"),
    ((set_name, "setName"))
    ((get_name, "getName")))

// 在 .cpp 中实现内建模块
MC_BUILTIN_MODULE_IMPL(mc_devices);

// 使用模块
int main() {
    auto devices_module = mc::load_module("mc.devices");
    if (!devices_module) {
        elog("无法加载设备模块");
        return -1;
    }
    auto factory = devices_module->get_factory();
    auto sensor_obj = factory->try_create_object("Sensor");
    sensor_obj->invoke_method("setName", {mc::variant("温度传感器")});
    return 0;
}

2.6 D-Bus 通信

功能说明

D-Bus 模块提供对 D-Bus 协议的完整封装,支持 C++ 和 Lua 双层接口,实现类型安全的消息传递和信号订阅机制。支持同步和异步通信模式,基于 io_context 的事件驱动架构。

参数说明

方法参数说明
方法名入参类型出参类型描述取值范围
connection::open_system_bus(executor)io_context&connection打开系统总线连接需要权限
connection::open_session_bus(executor)io_context&connection打开会话总线连接需要 D-Bus 守护进程运行
conn.request_name(name, flags)string, uint32tuple<bool, optional<error>>请求总线名称格式如 org.example.Service
conn.send(msg)messagebool异步发送消息不等待回复
conn.send_with_reply_and_block(msg, timeout)message, durationmessage同步发送并阻塞等待回复timeout 默认 25s
conn.async_send_with_reply(msg, timeout)message, durationfuture<message>异步发送并返回 future非阻塞模式
conn.add_match(rule, cb, id)match_rule, callback, uint64void添加信号匹配并订阅信号订阅
conn.register_path(path, handler)string, handlervoid注册对象路径处理器用于提供 D-Bus 方法

返回值与异常

返回值含义触发条件处理建议
{true, nullopt}请求名称成功名称未被占用
{false, error}请求名称失败名称冲突、权限不足检查 error.message
future.get()获取异步回复异步操作完成超时处理

应用场景

  • BMC 组件间进程间通信
  • 信号订阅(如 NameOwnerChanged)
  • Redfish 数据的 D-Bus 属性查询
  • 系统服务之间的方法调用

限制条件

  • 依赖 D-Bus 守护进程正常运行
  • 需要 libdbus-1 底层库支持
  • 异步消息需要 io_context 事件循环运行

调试示例

代码调试
cpp
#include <mc/dbus/connection.h>
#include <mc/dbus/message.h>

mc::runtime::thread_pool executor(4);
executor.start();

auto conn = mc::dbus::connection::open_session_bus(executor);
conn.start();

// 请求服务名称
auto [success, err_opt] = conn.request_name("org.example.MyService");
if (!success) {
    std::cerr << "请求名称失败" << std::endl;
}

// 同步调用
auto msg = mc::dbus::message::new_method_call(
    "org.freedesktop.DBus", "/org/freedesktop/DBus",
    "org.freedesktop.DBus", "ListNames");
auto reply = conn.send_with_reply_and_block(msg, mc::seconds(5));

// 信号订阅
auto rule = mc::dbus::match_rule::new_signal("NameOwnerChanged", "org.freedesktop.DBus");
conn.add_match(rule, [](mc::dbus::message& msg) {
    std::cout << "收到信号: " << msg.get_member() << std::endl;
}, 1);

// 命令行调试 D-Bus
// busctl introspect org.example.MyService /org/example/MyService
// busctl call org.example.MyService /org/example/MyService org.example.MyService MethodName

2.7 MicroComponent 接口

功能说明

MicroComponent(微组件)是 openUBMC 的核心架构单元,每个 BMC 组件都继承自 micro_component_object,通过 7 大标准化 D-Bus 接口与系统进行交互。

接口D-Bus 接口名功能描述
基础接口bmc.kepler.MicroComponent基础属性(名称/版本/作者等)和健康检查
配置管理bmc.kepler.MicroComponent.ConfigManage配置导入导出、备份恢复、校验
调试接口bmc.kepler.MicroComponent.Debug调试控制台、一键收集、日志级别动态设置
平滑重启bmc.kepler.MicroComponent.RebootPrepare → Process → Action → Cancel 四阶段流程
热复位bmc.kepler.MicroComponent.Reset支持 warm/cold/factory 三种复位类型
日志限流bmc.kepler.Release.Maintenance动态控制日志输出频率
性能统计bmc.kepler.MicroComponent.Performance统计信号/RPC 收发数量,输出到 CSV

2.7.1 基础接口 (bmc.kepler.MicroComponent)

属性
属性名类型说明
Pidint32进程 ID
Namestring组件名称
Authorstring作者信息
Descriptionstring组件描述
Licensestring许可证信息
Statusstring组件状态
Versionstring版本号
方法参数说明
方法名入参类型出参类型描述取值范围
HealthCheckint32健康检查0=健康, 负数=异常(-1=关键资源不可用, -2=网络不可用)
使用示例
cpp
class my_component_object : public mc::app::micro_component_object {
public:
    void init(mc::string_view service_name) {
        mc::app::micro_component_object::init(service_name);
        m_mc_iface.m_status = "Running";
    }
};

class my_service : public mc::engine::service {
public:
    int32_t on_health_check() const override {
        if (check_resources()) return 0;  // 健康
        return -1;  // 不健康
    }
};

2.7.2 配置管理接口 (bmc.kepler.MicroComponent.ConfigManage)

方法参数说明
方法名入参类型出参类型描述
Export(type)stringstring导出配置,返回 JSON 字符串
Import(data, type)string, stringvoid导入配置
Backup(filepath)string[(string, string)]备份配置,返回备份文件列表
Recover(preserve_list)map<string, string>void恢复配置
Verify(data)stringstring校验配置,返回校验结果 JSON
GetPreservedConfig(preserve_flag)map<string, string>string获取保留配置
GetTrustedConfigstring获取信任配置
使用示例
cpp
mc::string on_export_config(mc::string type) override {
    mc::json config;
    config["my_setting"] = get_my_setting();
    return mc::string(config.dump(2));
}

void on_import_config(mc::string data, mc::string type) override {
    auto config = mc::json::parse(data);
    if (config.contains("my_setting")) {
        set_my_setting(config["my_setting"]);
    }
}

2.7.3 调试接口 (bmc.kepler.MicroComponent.Debug)

属性
属性名类型说明
DlogLevelstring调试日志级别(debug/info/notice/warn/error)
DlogTypestring调试日志类型(file/remote/local)
方法参数说明
方法名入参类型出参类型描述
SetDlogLevel(level, hours)string, uint8void设置调试日志级别及有效时长(小时)
Dump(filepath)stringvoid一键收集调试信息到指定目录
AttachDebugConsole(port)uint32void连接调试控制台
DetachDebugConsolevoid断开调试控制台
使用示例
cpp
void on_dump(mc::string filepath) override {
    mc::string my_info = mc::string::concat(filepath, "/my_info.json");
    mc::json info;
    info["connections"] = get_connection_count();
    mc::filesystem::write_file(my_info, info.dump(2));
}

2.7.4 平滑重启接口 (bmc.kepler.MicroComponent.Reboot)

方法参数说明
方法名入参类型出参类型描述
Prepareint32准备阶段:完成数据持久化
Processint32处理阶段:执行业务逻辑
Actionint32执行阶段:完成最终操作
Cancelvoid取消阶段:回滚所有操作

处理流程:Normal → Prepare → Process → Action → Normal,任意阶段失败可通过 Cancel 回滚。

使用示例
cpp
int32_t on_reboot_prepare() override {
    save_critical_data();
    flush_pending_operations();
    return 0;
}

void on_reboot_cancel() override {
    rollback_changes();
}

2.7.5 热复位接口 (bmc.kepler.MicroComponent.Reset)

方法参数说明
方法名入参类型出参类型描述
Prepare(reset_type)stringint32准备阶段
Action(reset_type)stringint32执行阶段
Cancel(reset_type)stringvoid取消阶段

复位类型:warm(热复位)、cold(冷复位)、factory(恢复出厂设置)。

2.7.6 日志限流接口 (bmc.kepler.Release.Maintenance)

方法参数说明
方法名入参类型出参类型描述
DlogLimit(enabled, duration_mins)bool, uint8void设置日志限流状态和时长(分钟)
  • enabled=true:启用日志限流(默认状态)
  • enabled=false, duration_mins=N:禁用限流 N 分钟后自动恢复
  • enabled=false, duration_mins=0:永久禁用限流

2.7.7 性能统计接口 (bmc.kepler.MicroComponent.Performance)

方法参数说明
方法名入参类型出参类型描述
StartProfiling(duration)uint32void启动性能统计,指定时长(分钟)

统计项:sent_signals(发送信号数)、received_signals(接收信号数)、sent_rpcs(发送 RPC 数)、received_rpcs(接收 RPC 数)。结果存储于 /dev/shm/profiling.csv

返回值与异常

返回值含义触发条件处理建议
0健康/成功组件运行正常
-1不健康/失败关键资源不可用检查组件状态和日志
-2网络不可用网络连接异常检查网络配置

应用场景

  • BMC 组件的标准化开发和运行管理
  • 通过 mdbctl 命令行工具进行远程调试和诊断
  • 组件健康状态监控与告警
  • 平滑重启场景下的数据持久化和恢复
  • 生产环境日志限流与性能分析

调试示例

命令行调试
bash
# 查看组件信息
busctl introspect org.openubmc.my_component /bmc/kepler/my_component/MicroComponent

# 健康检查
busctl call org.openubmc.my_component \
  /bmc/kepler/my_component/MicroComponent \
  bmc.kepler.MicroComponent HealthCheck

# 设置调试日志级别(有效期1小时)
busctl call org.openubmc.my_component \
  /bmc/kepler/my_component/MicroComponent \
  bmc.kepler.MicroComponent.Debug SetDlogLevel a{ss}sy 0 debug 1

# 一键收集调试信息
busctl call org.openubmc.my_component \
  /bmc/kepler/my_component/MicroComponent \
  bmc.kepler.MicroComponent.Debug Dump s /tmp/my_dump

# 启动性能统计(60分钟)
busctl call org.openubmc.my_component \
  /bmc/kepler/my_component/MicroComponent \
  bmc.kepler.MicroComponent.Performance StartProfiling q 60

# 禁用日志限流(30分钟后恢复)
busctl call org.openubmc.my_component \
  /bmc/kepler/my_component/MicroComponent \
  bmc.kepler.Release.Maintenance DlogLimit by 0 30

# 使用 mdbctl 工具
mdbctl set-log-level --service org.openubmc.my_component --level debug --hours 1
mdbctl dump --service org.openubmc.my_component --path /tmp/my_dump
mdbctl profiling --service org.openubmc.my_component --duration 60

2.8 数据校验 API

功能说明

mc::validate::validator 提供面向 C++ 的通用参数校验能力,支持对对象属性数据的类型、取值范围、字符串长度、正则格式和 JSON 格式进行语义和语法校验。校验失败时抛出分类明确的异常,异常消息格式与 Lua 模块 lvalidate 保持一致,便于 C++ 与 Lua 两侧复用同一套校验规则。

参数说明

方法参数说明
方法名入参类型出参类型描述取值范围
check_integer(name, val, min, max, need_convert)string_view, double, double, double, boolvoid校验整数值及范围val 须为整数值且在 [min, max] 内
ranges(name, val, min, max, need_convert, allow_nil)string_view, double, double, double, bool, boolvoid校验数值范围val 在 [min, max] 内
lens(name, val, min, max, need_convert, allow_nil)string_view, string_view, int, int, bool, boolvoid校验字符串长度长度在 [min, max] 内
regex(name, val, pattern, need_convert, allow_nil)string_view, string_view, string_view, bool, boolvoid校验正则匹配pattern 为 GLib 正则表达式
json(val)string_viewvoid校验 JSON 格式合法 JSON 字符串

need_convert 控制错误消息格式是否与 Lua lvalidate 一致。allow_nil 用于对齐 Lua 的 nil 语义。

返回值与异常

异常类型含义触发条件处理建议
property_value_type_error类型不符合期望整数但传入浮点数或 NaN检查属性值类型,确保传入整数值
property_value_out_of_range数值超出范围val 不在 [min, max] 区间检查属性取值范围,确保在合法范围内
string_length_error字符串长度不满足约束字符串长度不在 [min, max] 区间检查字符串长度限制
format_error格式不匹配正则不匹配或正则编译失败检查正则表达式和待校验字符串
json_errorJSON 解析失败JSON 字符串格式非法检查 JSON 字符串语法

所有异常均继承自 mc::validate::validation_exception,并最终继承 std::runtime_error

应用场景

  • Redfish 接口入参校验(如 PATCH 请求的属性值合法性检查)
  • 配置文件加载后的合法性验证
  • D-Bus 方法调用参数的预处理校验
  • 与 Lua 模块配合,实现 C++/Lua 统一的校验规则

限制条件

  • 正则匹配基于 GLib 的 GRegex,需链接 glib 库
  • allow_nil 参数用于 Lua 互操作,C++ 侧无 nil 概念,建议使用 std::optional<T> 或调用前自行判断

调试示例

代码调试
cpp
#include <mc/validate/validator.h>

void validate_request(std::string_view user, double age, std::string_view payload) {
    // 校验整数 + 范围:age 必须为 0-200 的整数
    mc::validate::validator::check_integer("Age", age, 0, 200, false);

    // 校验字符串长度:用户名 1-64 字符
    mc::validate::validator::lens("User", user, 1, 64, false);

    // 校验 JSON 格式
    mc::validate::validator::json(payload);

    // 校验正则:MAC 地址格式
    mc::validate::validator::regex("MAC", "AA:BB:CC:DD:EE:FF",
        "^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$", false);
}

// 异常处理
try {
    mc::validate::validator::check_integer("Prop1", 3.14, 0, 100, false);
} catch (const mc::validate::validation_exception& e) {
    // e.what() 的文案与 Lua lvalidate 报错保持一致
    elog("校验失败: ${error}", ("error", e.what()));
}

2.9 对象代理访问 API

功能说明

proxy_object 类提供了 D-Bus 对象的代理封装,允许通过 C++ 接口访问远程 D-Bus 对象的属性和方法。该类封装了 D-Bus 通信细节,提供类型安全的属性访问(获取/设置/批量查询)和方法调用(同步/安全调用)接口,是构建对象和属性联接的核心组件。

参数说明

属性参数说明
属性名类型读写说明
servicestring只读目标 D-Bus 服务名称
pathstring只读目标 D-Bus 对象路径
interfacestring只读目标 D-Bus 接口名称
方法参数说明
方法名入参类型出参类型描述取值范围
get_property(name)stringmc::variant获取单个属性值属性名
set_property(name, value)string, mc::variantvoid设置属性值(自动类型校验)value 类型需匹配接口定义
get_all_properties()voidmc::dict获取所有属性值返回属性名→值的字典
get_properties(prop_names)mc::variantspair<mc::dict, mc::dict>批量获取指定属性返回(成功字典, 错误字典)
call_method(name, args)string, mc::variantmc::variants调用 D-Bus 方法(抛异常)args 须为数组类型 variant
call_method_pcall(name, args, error)string, mc::variant, optional<string>&mc::variants安全调用方法(不抛异常)失败时 error 包含错误信息
has_property(name)stringbool检查属性是否存在
has_method(name)stringbool检查方法是否存在
is_volatile(name)stringbool检查属性是否为易变属性

返回值与异常

返回值含义触发条件处理建议
get_property 成功返回 variant属性值获取成功属性存在且目标服务可访问使用 as<T>() 转换到目标类型
mc::exception 异常属性不存在属性名未在接口中定义使用 has_property() 预先检查
mc::exception 异常方法不存在方法名未在接口中定义使用 has_method() 预先检查
call_method_pcall 返回空列表方法调用失败参数类型不匹配或服务端错误检查 error 参数获取详细信息

应用场景

  • 在 MDB(管理数据库)中访问其他 BMC 组件的 D-Bus 对象属性
  • Redfish 资源查询时通过代理对象获取底层硬件状态
  • 跨服务的方法调用(如更新固件、查询传感器数据)
  • 批量读取对象属性以提高性能(替代多次 get_property 调用)

限制条件

  • proxy_object 不是线程安全的,多线程环境需外部同步
  • 每次属性访问和方法调用都会进行 D-Bus 通信,高频场景建议使用 get_all_properties 批量获取
  • 依赖底层 sd_bus 连接,需确保 D-Bus 守护进程正常运行

调试示例

代码调试
cpp
// 代理对象通过代理接口实现,示例如下
namespace mc::app::discovery {

class object_group_proxy : public mc::engine::interface_proxy<object_group_proxy> {
public:
MC_INTERFACE_PROXY("bmc.kepler.ObjectGroup");

MC_PROXY_METHOD(get_binary_objects_response, GetBinaryObjects, "a{ss}s",
                ((mc::dict, filters))((mc::string, owner)));

};

}

2.10 任务管理机制

功能说明

mc::app::task::TaskMgmt 提供通用的任务资源管理机制,遵循 Redfish TaskService 规范,支持任务的创建、进度跟踪、状态更新和自动回收。任务以 D-Bus 对象的形式暴露(接口 bmc.kepler.TaskService.Task),便于外部系统通过 D-Bus 查询和监控异步任务的执行状态。

参数说明

属性参数说明

任务 D-Bus 对象包含以下属性:

属性名类型读写说明
Iduint32只读任务唯一标识
Namestring只读任务名称
StartTimestring只读开始时间(ISO 8601)
EndTimestring只读结束时间(ISO 8601)
Statestring读写任务状态(New/Running/Completed/Exception/Cancelled 等)
Statusstring读写健康状态(OK/Warning/Error/Critical)
Progressuint32读写进度百分比(0-100)
EstimatedDurationuint32读写预计持续时间(分钟)
Stagestring读写当前阶段描述
Descriptionstring读写任务描述
MessageIdstring读写消息 ID
Parametersstring读写任务参数(JSON)
任务状态枚举
状态说明
New初始状态,任务刚创建
Starting任务正在启动
Running任务正在执行
Suspended任务已挂起
Interrupted任务被中断
Pending任务等待中
Stopping任务正在停止
Completed任务已完成
Killed任务被杀死
Exception任务异常
Cancelled任务已取消

自动关联规则:设置 State = Completed 时自动将 Progress 设为 100;设置 Progress = 100 时自动将 State 设为 Completed

方法参数说明
方法名入参类型出参类型描述
instance()voidTaskMgmt&获取单例实例
init(service)dbus::service*void初始化任务管理器(使用前必须调用一次)
create_task(name, parent_path, timeout_mins, timeout_cb, props)string, string, optional<int>, optional<callback>, optional<dict>tuple<CreateResult, string, task_id>创建任务,返回(结果, 错误信息, 任务ID)
update_task(id, data)task_id, dictUpdateResult更新任务属性(Progress/State/Status 等)
destroy_task(id)task_idvoid销毁任务,释放 D-Bus 对象
get_task_obj(id)task_idtask_object*获取任务对象指针
get_task_count()voidsize_t获取当前任务数量
get_all_task_ids()voidvector<task_id>获取所有任务 ID 列表
set_auto_delete_timeout(mins)intvoid设置已结束任务的自动删除超时(10-1440 分钟,默认 10 分钟)

返回值与异常

返回值含义触发条件处理建议
CreateResult::Success (0)任务创建成功任务数量未达上限、路径合法task_id 可用于后续操作
CreateResult::MaxTasksReached (-1)创建失败任务数达上限(32)、未初始化或路径无效等待已有任务完成或检查 parent_path 格式
CreateResult::TimeoutOutOfRange (-2)创建失败超时值为负数使用非负数超时时间
UpdateResult::Success (0)更新成功任务存在、数据合法
UpdateResult::TaskNotFound (-1)更新失败任务 ID 不存在检查任务是否已被销毁
UpdateResult::TaskEnded (-9)更新失败任务已结束(Completed/Exception/Cancelled)已结束任务不可更新

生命周期

create_task() → [New] → update_task(State=Running) → [Running]
    → update_task(Progress=100) 或 update_task(State=Completed/Exception/Cancelled)
    → [已结束, alive=false] → destroy_timer 触发(默认 10 分钟后)→ 自动释放

关键机制:

  • 超时自动结束:创建时指定 timeout_minutes,超时后自动将任务标记为已结束
  • 延迟销毁:任务结束后默认 10 分钟后自动释放 D-Bus 对象和内部资源
  • 满时回收:任务数量达到上限(32)时自动清理最早结束的任务
  • 并发安全:全局 m_tasks_mutex 保护任务表,每个 TaskEntry 有独立锁保护更新操作

应用场景

  • 固件升级流程跟踪(创建升级任务 → 更新进度 → 完成/异常)
  • 配置导入导出等长时间操作的进度报告
  • Redfish TaskService 资源的标准实现
  • 日志收集、诊断信息导出等异步操作的状态管理

限制条件

  • 单次最多创建 32 个活跃任务
  • 任务 parent_path 必须以 / 开头,符合 D-Bus 对象路径规范
  • 需在服务启动时调用 init() 一次,传入 D-Bus service 指针

调试示例

代码调试
cpp
#include <mc/app/task/task_mgmt.h>

auto& mgmt = mc::app::task::TaskMgmt::instance();
mgmt.init(&service);  // 首次初始化

// 创建固件升级任务
auto [result, error, task_id] = mgmt.create_task(
    "FirmwareUpdate",                         // 任务名称
    "/bmc/kepler/UpdateService",              // 父路径(必须以'/'开头)
    60,                                        // 超时 60 分钟
    nullptr,                                   // 超时回调(可选)
    {{"EstimatedDuration", 10}, {"Description", "BMC固件升级"}}  // 初始属性
);

if (result == mc::app::task::CreateResult::Success) {
    // 启动任务
    mgmt.update_task(task_id, {{"State", "Running"}, {"Stage", "验证镜像"}});

    // 更新进度
    for (int i = 0; i <= 100; i += 10) {
        mgmt.update_task(task_id, {{"Progress", i}});
        // 执行实际升级工作...
    }

    // 完成
    mgmt.update_task(task_id, {{"State", "Completed"}});  // Progress 自动设为 100
}

// D-Bus 路径: /bmc/kepler/UpdateService/TaskService/Tasks/{task_id}
命令行调试 D-Bus
bash
# 查看任务列表
busctl introspect org.openubmc.my_component /bmc/kepler/UpdateService/TaskService

# 查看任务属性
busctl introspect org.openubmc.my_component /bmc/kepler/UpdateService/TaskService/Tasks/1

# 查询单个属性
busctl get-property org.openubmc.my_component \
  /bmc/kepler/UpdateService/TaskService/Tasks/1 \
  bmc.kepler.TaskService.Task Progress

2.11 业务对象框架

功能说明

业务对象框架是 libmcpp 的核心编程模型,提供从接口定义、对象声明、生命周期管理到实例注册上树的完整能力。开发者通过继承 mc::app::object<T> 定义业务对象,使用 MC_OBJECT / MC_INTERFACE 宏声明 D-Bus 元数据,框架自动管理对象的 6 个生命周期阶段(init → post_init → register → ready → unregister → unready),并通过 service::register_object() 将对象注册到服务对象树中。

核心概念

接口定义(mc::engine::interface

每个业务对象可包含多个接口,每个接口对应一个 D-Bus 接口定义:

cpp
class sensor_iface : public mc::engine::interface<sensor_iface> {
public:
    MC_INTERFACE("bmc.example.Sensor")  // D-Bus 接口名称和版本
    double Value{0.0};
    mc::string Unit;
};
MC_REFLECT(sensor_iface, ((Value, "Value"))((Unit, "Unit")))
对象声明(mc::app::object<T>

通过 MC_OBJECT 宏声明对象的类名、D-Bus 路径模板和接口列表:

cpp
class sensor : public mc::app::object<sensor> {
public:
    MC_OBJECT(sensor, "Sensor",
              "/bmc/example/Sensors/${object_name}",
              (sensor_iface))

    sensor_iface iface;

    void on_init(const mc::dict& props) override {
        mc::engine::object_impl::on_init(props);  // 通过反射回填属性
    }
};
MC_REFLECT(sensor, ((iface, "iface")))
对象生命周期钩子
钩子触发时机信号状态对象已注册D-Bus 可见典型用途
on_init(props)init 阶段,同步执行信号抑制中属性回填、设置默认值、声明 wait_for 依赖
on_post_init()on_init 后、注册前,同步执行信号可外发派生属性计算、内部数据结构准备
on_register()加入对象树后,异步 post信号可外发确认 D-Bus 路径、注册 match 规则
on_ready()register 后自动触发,异步 post信号可外发启动后台任务、发送业务信号
on_unready()需业务显式调用 unready()信号可外发清理后台任务
on_unregister()从对象树移除前,异步 post信号可外发释放资源、断开外部连接
对象注册与实例管理

对象通过 service::register_object() 注册到服务对象树,核心流程为:

  1. 将对象加入 service_object_table(以 D-Bus 路径为键)
  2. 沿路径向上查找父对象,建立 owner 父子关系
  3. 触发 on_registeron_ready 生命周期钩子
  4. 发出 InterfacesAdded D-Bus 信号(仅在 service started 后)

解注册通过 service::unregister_object(path) 对称执行:递归解注册子对象 → 从对象树移除 → 触发 on_unregister

InterfacesAdded / InterfacesRemoved 信号从父对象的 D-Bus 路径发出(遵循 D-Bus ObjectManager 规范)。

参数说明

对象注册方法
方法名入参类型出参类型描述
service::register_object(obj)abstract_object&void注册对象到服务对象树
service::unregister_object(path)stringvoid解注册指定路径的对象(递归移除子对象)
obj.register_object()voidvoid对象自行注册(需已关联 service)
obj.unregister_object()voidvoid对象自行解注册
obj.defer_registration()voidvoid延迟注册(dispatcher 不自动 register,业务自行决定时机)
obj.defer_ready()voidvoid延迟就绪(on_register 后不自动 ready,业务显式调用 ready())
obj.ready()voidvoid使对象进入就绪状态(幂等安全)
obj.unready()voidvoid使对象退出就绪状态
属性初始化方法
方法名入参类型出参类型描述
on_init(props)const mc::dict&void初始化回调,props 包含派发数据,信号抑制期内执行。业务不应该重写on_init接口
get_property(name, default, index)string, mc::variant, intmc::variant获取初始化属性(支持默认值)
ensure_default(name, default)string, Tvoid确保属性有默认值(属性为空时填充)

返回值与异常

返回值含义触发条件处理建议
register_object 成功对象已上树路径合法、service 已注册对象对 D-Bus 可见(service started 后)
MC_ASSERT 断言失败路径非法路径为空或不符合 D-Bus 对象路径规范检查对象路径模板和实际展开值
对象未创建MC_OBJECT 类名不匹配自发现场景下 class_name 未注册确认 register_object_types() 中已调用 metadata()

应用场景

  • 定义 BMC 硬件组件的 D-Bus 对象(传感器、网络适配器、固件等)
  • 自发现服务创建和管理硬件对象
  • 对象间的父子层级关系管理
  • 属性默认值填充和生命周期精细控制

调试示例

代码调试
cpp
#include <mc/engine/object.h>
#include <mc/engine/service.h>

// 1. 定义接口
class my_iface : public mc::engine::interface<my_iface> {
public:
    MC_INTERFACE("bmc.example.MyInterface")
    mc::string Name;
    int Value{0};
};
MC_REFLECT(my_iface, ((Name, "Name"))((Value, "Value")))

// 2. 定义业务对象
class my_object : public mc::app::object<my_object> {
public:
    MC_OBJECT(my_object, "MyObject",
              "/bmc/example/MyObjects/${object_name}",
              (my_iface))

    explicit my_object(mc::engine::core_object* parent = nullptr)
        : mc::app::object<my_object>(parent) {}

    my_iface iface;

protected:
    void on_init(const mc::dict& props) override {
        // 信号抑制期:设置默认值、回填属性
        ensure_default("Name", mc::string("default"));
        ensure_default("Value", 0);
        mc::engine::object_impl::on_init(props);
    }

    void on_register() override {
        // 对象已上树,获取 D-Bus 路径
        ilog("对象已注册: ${path}", ("path", get_object_path()));
    }

    void on_ready() override {
        // 对象完全就绪,InterfacesAdded 已发出
        ilog("对象已就绪: ${path}", ("path", get_object_path()));
        // 可启动后台轮询、订阅事件等
    }

    void on_unregister() override {
        // 清理资源
        ilog("对象已移除: ${path}", ("path", get_object_path()));
    }
};
MC_REFLECT(my_object, ((iface, "iface")))

// 3. 注册对象
void register_objects(mc::engine::service& svc) {
    auto obj = mc::make_shared<my_object>(nullptr);
    obj->init(props);  // 触发 on_init → on_post_init
    obj->set_object_path("/bmc/example/MyObjects/obj_1");
    svc.register_object(*obj);  // 触发 on_register → on_ready
}
命令行调试 D-Bus
bash
# 确认对象已注册
busctl introspect org.openubmc.my_service /bmc/example/MyObjects/obj_1

# 查看对象属性
busctl get-property org.openubmc.my_service \
  /bmc/example/MyObjects/obj_1 \
  bmc.example.MyInterface Name

# 监听对象 InterfacesAdded/Removed 信号
busctl monitor --match "type='signal',interface='org.freedesktop.DBus.ObjectManager'"

3. 组件扩展案例

3.1 扩展能力概述

libmcpp 框架提供以下扩展能力,允许开发者根据业务需求进行定制和扩展:

  • 自定义服务开发:通过继承 mc::service_base 实现业务服务,注册到服务工厂后由框架统一管理生命周期(init → start → run → stop → cleanup)
  • 自定义插件开发:通过继承 mc::plugin 实现插件模块,支持内建模块(静态链接)和动态模块(dlopen 加载 .so 文件)两种方式
  • 自发现服务开发:通过继承 mc::app::discovery_service 实现自发现服务,自动接收 hwdiscovery 派发的对象组并创建业务对象
  • 自定义异常类型:通过继承 mc::exception 定义业务特定异常,注册到异常工厂(exception_factory)后支持动态创建、序列化和跨模块传递

3.2 扩展点说明

扩展点基类注册方式加载方式适用场景
自定义服务mc::service_base<T>factory.register_service<T>()插件内注册,框架管理生命周期业务功能模块
自定义插件mc::plugin / mc::plugin_base<T>app.register_plugin<T>()MC_EXPORT_PLUGIN静态链接或 dlopen 动态加载 .so功能模块打包与热插拔
自发现服务mc::app::discovery_serviceregister_object_types() + metadata()继承 discovery_service,随应用启动硬件对象自动发现与管理
自定义异常mc::exceptionexception_factory::instance().register_exception<T>()程序初始化时注册业务异常分类处理与跨模块传递

3.3 二次开发指导

步骤一:自定义服务开发

通过继承 mc::service_base 实现自定义服务,定义服务类型名、配置选项和依赖关系:

cpp
class my_service : public mc::service_base<my_service> {
public:
    static const char* service_type() { return "example.my_service"; }

    static void register_options(mc::po::options_description& cfg_opts) {
        cfg_opts.add_options()
            ("my_service.timeout", mc::po::value<int>()->default_value(30), "超时时间")
            ("my_service.retry_count", mc::po::value<int>()->default_value(3), "重试次数");
    }

    static const std::vector<std::string>& dependencies() {
        static std::vector<std::string> deps = {"logger", "database"};
        return deps;
    }

    bool init(mc::dict args) override {
        m_timeout = args.get("my_service.timeout").as<int>();
        return true;
    }

    bool start() override {
        set_state(mc::service_state::running);
        return true;
    }

    bool stop() override {
        set_state(mc::service_state::stopped);
        return true;
    }

    bool is_healthy() const override { return true; }

private:
    int m_timeout;
};

步骤二:自定义插件开发

通过继承 mc::plugin 实现自定义插件,注册服务类型并使用 MC_EXPORT_PLUGIN 导出:

cpp
class example_plugin : public mc::plugin {
public:
    const mc::plugin_info& get_info() const override {
        static mc::plugin_info info = {
            .name = "example",
            .version = "1.0.0",
            .dependencies = {"core", "logger"}
        };
        return info;
    }

    bool init(mc::service_factory& factory) override {
        factory.register_service<my_service>();
        return true;
    }
};

MC_EXPORT_PLUGIN(example_plugin)

步骤三:自发现服务开发

通过继承 mc::app::discovery_service 实现自发现服务,完成业务对象的创建和生命周期管理:

cpp
class network_adapter_iface : public mc::engine::interface<network_adapter_iface> {
public:
    MC_INTERFACE("bmc.kepler.Systems.NetworkAdapter")
    std::uint8_t SlotNumber{0};
    mc::string Name;
};
MC_REFLECT(network_adapter_iface, ((SlotNumber, "SlotNumber"))((Name, "Name")))

class network_adapter : public mc::app::object<network_adapter> {
public:
    MC_OBJECT(network_adapter, "NetworkAdapter",
              "/bmc/kepler/Systems/${SystemId}/NetworkAdapters/${object_name}",
              (network_adapter_iface))

    network_adapter_iface iface;

    void on_init(const mc::dict& props) override {
        mc::engine::object_impl::on_init(props);
    }

    void on_ready() override {
        // 对象就绪,可启动后台任务
    }

    void on_unregister() override {
        // 清理资源
    }
};
MC_REFLECT(network_adapter, ((iface, "iface")))

class my_discovery_service : public mc::app::discovery_service {
public:
    explicit my_discovery_service(mc::string name)
        : mc::app::discovery_service(std::move(name)) {}

protected:
    void register_object_types() override {
        (void)network_adapter::metadata();
    }

    mc::app::discovery::service_object_group_source::config
    make_service_source_config() const override {
        auto cfg = mc::app::discovery_service::make_service_source_config();
        cfg.get_binary_objects_owner = "network_adapter";
        return cfg;
    }
};

步骤四:自定义异常类型

通过继承 mc::exception 定义自定义异常,并注册到异常工厂:

cpp
class my_exception : public mc::exception {
public:
    enum code_enum { code_value = MY_EXCEPTION_CODE };

    my_exception(mc::log_message&& msg = mc::log_message(
        mc::log::level::error, "我的异常"))
        : exception(std::move(msg), code_value, "my_exception", "我的异常描述") {}

    my_exception(const my_exception& e) : exception(e) {}
    my_exception(my_exception&& e) : exception(std::move(e)) {}
    explicit my_exception(const mc::exception& e) : exception(e) {}

    std::shared_ptr<mc::exception> dynamic_copy_exception() const override {
        return std::make_shared<my_exception>(*this);
    }

    void dynamic_rethrow_exception() const override { throw *this; }
};

// 注册异常类型
mc::exception_factory::instance().register_exception<my_exception>();

3.4 自发现加载与卸载机制

自发现服务通过 dispatcher 自动管理业务对象的加载(发现 → 创建 → 注册)和卸载(消失 → 解注册 → 销毁)全生命周期:

加载流程(对象出现时):

hwdiscovery 派发 ObjectGroup 信号
    → service_object_group_source 接收数据
    → dispatcher::on_source_update() 编排对象
    → dispatcher::dispatch_object():解码属性 → factory_hook.create() → obj.init(props)
    → register_or_defer():
        ├─ [无依赖] 立即 service->register_object() → 对象上树
        ├─ [有 required 依赖] 登记到 dependency_tracker,依赖满足后 register
        └─ [defer_registration] 业务自行决定注册时机
    → on_register → on_ready(生命周期钩子链)

卸载流程(对象消失时):

hwdiscovery 派发 ObjectGroup 信号(该对象不再出现)
    → dispatcher::on_source_update() 发现对象缺失
    → dispatcher::on_object_disappear(key)
    → service->unregister_object(path)
        ├─ 递归解注册所有子对象
        ├─ 触发 on_unregister() 生命周期钩子
        ├─ 从 object_table 移除
        ├─ 清除 owner 关系
        └─ emit InterfacesRemoved D-Bus 信号

三种注册策略

条件行为说明
无 required 依赖立即 service->register_object()最常见路径
有 required 依赖登记到 tracker,依赖满足后回调注册确保依赖对象先注册
业务调 defer_registration()dispatcher 不主动 register业务自行管理注册时机

依赖声明示例

cpp
void on_init(const mc::dict& props) override {
    // required: 依赖未满足时不 register
    wait_for("#/External.RequiredService", mc::app::wait_kind::required);

    // optional: 不阻塞 register
    wait_for("#/External.OptionalService", mc::app::wait_kind::optional);

    mc::engine::object_impl::on_init(props);
}

关键要点:

  • 自发现对象的 register_object() 走的是与直接注册完全相同的核心链路
  • 对象移除时自动递归解注册所有子对象
  • on_unregister() 中应完成资源清理(关闭文件描述符、停止后台任务、断开连接)
  • 若业务实现了 on_unready() 逻辑,需在 on_unregister() 中显式调用 unready()

4. 日志说明

4.1 一键日志收集

文件路径内容说明
/var/log/app.log应用程序运行日志
/var/log/mcpp/*.loglibmcpp 框架内部日志
/tmp/dump_*/Dump 接口一键收集调试信息目录
/dev/shm/profiling.csv性能统计数据文件

4.2 关键日志信息

日志片段日志级别含义解读建议处理动作
NOTIC xxx on_init: ... path=...NOTICE业务对象的 on_init 生命周期回调被调用用于判断对象创建是否正常触发
NOTIC xxx on_register: ... path=...NOTICE业务对象已注册到 D-Bus 对象表确认对象注册成功
NOTIC xxx on_ready: ... path=...NOTICE业务对象已完全就绪,InterfacesAdded 已发出确认对象对外可见
NOTIC xxx on_unregister: ... path=...NOTICE业务对象已从 D-Bus 移除确认对象生命结束,检查资源清理
ERROR ... Connection timeoutERRORD-Bus 连接或方法调用超时检查目标服务是否正常运行
ERROR ... request name failedERRORD-Bus 名称请求失败检查名称是否被占用或权限不足
WARN ... plugin load failedWARN插件加载失败检查 .so 文件是否存在、依赖是否满足
ERROR ... MC_THROW ...ERROR异常被抛出根据异常信息和错误码定位根因

5. 问题定界指南

5.1 典型问题定界

现象描述是否为本组件问题判断依据关键证据收集方法
应用程序启动失败可能是检查配置文件和插件是否正常加载查看启动日志,检查 config.json 语法
插件加载失败插件 .so 文件不存在或依赖缺失查看 PluginManager 日志,使用 ldd 检查依赖
服务无法启动可能是检查服务依赖是否满足、配置是否正确查看 ServiceManager 日志,检查依赖关系
D-Bus 方法调用超时可能是检查目标服务是否运行、D-Bus 守护进程状态使用 busctl 检查服务可用性,查看 D-Bus 错误
日志未输出检查日志级别配置和 Appender 配置检查 LogManager 配置,确认日志级别阈值
共享内存数据库访问失败检查共享内存权限和状态使用 ipcs 查看共享内存段状态
自发现服务未收到对象组可能是检查 hwdiscovery 服务状态和 allowed_owners 配置查看 D-Bus 信号总线,确认 ObjectGroup 信号
反射序列化结果不正确检查 MC_REFLECT 宏注册是否正确对比实际输出和预期 JSON 格式
组件健康检查失败HealthCheck 返回非 0 值查看组件内部状态,检查关键资源

5.2 错误码速查表

libmcpp 框架异常通过 mc::exception 基类的 code() 方法返回异常代码,异常代码定义在 exception_code 枚举中。常见异常代码及排查建议如下:

错误码含义可能原因排查建议
0未知异常未指定具体异常类型的通用错误查看异常消息 e.what() 获取详细信息
1未处理的第三方异常捕获了标准库或第三方库的异常,未映射为框架异常使用 MC_CAPTURE_AND_WRAP_EXCEPTION 包装第三方异常
2超时异常D-Bus 调用超时、异步操作超时、网络请求超时增大超时时间,检查目标服务是否正常运行
3文件未找到配置文件路径错误、插件 .so 文件缺失检查文件路径,确认文件是否存在及权限是否正确
4解析错误JSON 格式错误、配置文件语法错误、证书格式错误使用 JSON 校验工具检查格式,对照配置模板修正
5无效参数方法调用传入了类型不匹配或范围不正确的参数检查参数类型和取值范围,使用 variant::as<T>() 时确认类型
6键未找到dict 中访问不存在的键、配置项缺失使用 dict::get() 替代直接下标访问,或先检查 has()
7类型转换异常variant 类型不匹配的转换、std::bad_variant_access使用 variant::is<T>() 预先检查类型
8越界异常数组或容器访问超出范围使用 at() 替代 [],检查容器大小
9取消操作异常异步任务被取消、用户主动终止操作正常情况,检查取消操作的触发条件
10断言异常MC_ASSERT 宏检查失败检查断言条件,确认前置条件是否满足
14无效操作异常在不正确的状态下调用方法(如服务未启动时调用 stop)检查调用时序,确认对象当前状态是否允许该操作
21繁忙异常资源被占用、并发操作冲突等待重试或检查资源锁状态
23未实现异常调用了未实现的方法或接口检查虚函数是否正确重写,确认功能是否已实现
27未找到异常请求的服务、插件或对象不存在检查名称是否拼写正确,确认目标是否已注册
31权限不足D-Bus 操作权限不足、文件访问权限不足检查 D-Bus 策略配置,确认文件权限

5.3 调试方法

开启调试日志

bash
# 开启 libmcpp 框架详细日志
busctl call org.openubmc.my_component \
  /bmc/kepler/my_component/MicroComponent \
  bmc.kepler.MicroComponent.Debug SetDlogLevel a{ss}sy 0 debug 1

# 通过 mdbctl 设置
mdbctl set-log-level --service org.openubmc.my_component --level debug --hours 1

# 查看实时日志
tail -f /var/log/app.log | grep -E 'NOTIC|ERROR|WARN'

# 程序内设置日志级别
#include <mc/log.h>
mc::log::default_logger().set_level(mc::log::level::debug);

D-Bus 调试方法

bash
# 查看服务接口
busctl introspect org.openubmc.my_component /bmc/kepler/my_component/MicroComponent

# 查看系统总线上所有服务
busctl list

# 监听 D-Bus 信号
busctl monitor --match "type='signal'"

# 调用方法
busctl call org.openubmc.my_component \
  /bmc/kepler/my_component/MicroComponent \
  bmc.kepler.MicroComponent HealthCheck

复现问题方法

前置条件设置:

  1. 确保 libmcpp 应用程序服务正常运行
  2. 确保 D-Bus 守护进程正常运行
  3. 配置文件路径正确

操作步骤:

  1. 使用 mdbctl set-log-level --level debug 开启详细日志
  2. 执行触发问题的操作
  3. 使用 mdbctl dump --path /tmp/dump 收集调试信息
  4. 分析日志和调试信息定位问题

预期结果:

  • 日志显示完整的操作流程和异常信息
  • dump 目录包含所有相关调试数据

6. 常见问题解答

Q1:为什么插件加载失败?

  • 问题描述 应用程序启动时,插件管理器(PluginManager)报告插件加载失败,服务不可用。

  • 一句话答案 检查插件 .so 文件是否存在、依赖库是否满足、版本兼容性是否匹配。

  • 根因说明 可能原因包括:插件动态库文件路径不正确、插件依赖的其他 .so 库缺失或版本不匹配、插件签名验证失败、插件目录不存在或权限不足。

  • 解决方案

    1. 确认插件 .so 文件存在于 PluginManager 的 plugin_dir 目录
    2. 使用 ldd plugin.so 检查插件的动态库依赖是否满足
    3. 检查插件版本与框架版本的兼容性
    4. 检查文件权限,确保应用程序有读取权限
    5. 开启 debug 日志查看详细的加载失败原因
  • 规避方案 将核心功能实现为内建模块(静态链接),减少对动态加载的依赖。

  • 适用版本 openUBMC 1.0.0 以上

Q2:D-Bus 方法调用超时怎么处理?

  • 问题描述 使用 send_with_reply_and_blockasync_send_with_reply 调用 D-Bus 方法时超时,未收到回复。

  • 一句话答案 检查目标服务是否正在运行、方法名和接口名是否正确、超时时间是否足够。

  • 根因说明 目标服务可能未启动或已崩溃、方法调用参数格式错误导致服务端处理异常、超时时间设置过短、io_context 事件循环未运行导致消息未分发。

  • 解决方案

    1. 使用 busctl list 确认目标服务在 D-Bus 总线上注册
    2. 使用 busctl introspect 确认方法名和接口名正确
    3. 检查 io_context 是否正确启动(executor.start()
    4. 适当增大超时时间(如 mc::seconds(30)
    5. 使用 busctl monitor 观察消息是否发送成功
  • 规避方案 使用异步模式 async_send_with_reply 配合 future,避免阻塞主线程。

  • 适用版本 openUBMC 1.0.0 以上

Q3:日志没有输出到期望的文件?

  • 问题描述 代码中调用了日志宏(ilog、elog 等),但日志文件未生成或没有内容。

  • 一句话答案 检查 LogManager 配置、Appender 注册和日志级别阈值。

  • 根因说明 可能原因:未注册对应的 Appender、日志级别阈值高于当前日志级别导致被过滤、日志文件路径无写入权限、异步日志缓冲区未刷新。

  • 解决方案

    1. 确认已调用 MC_LOG_REGISTER_APPENDER 注册 Appender
    2. 确认已调用 setLevel() 设置合适的日志级别
    3. 检查日志文件路径的写入权限
    4. 使用 ilog("系统启动") 进行测试
    5. 程序退出前确保调用日志系统的 flush 或清理操作
  • 规避方案 在开发阶段使用 ConsoleAppender 直接输出到控制台,避免文件权限问题。

  • 适用版本 openUBMC 1.0.0 以上

Q4:自发现服务没有创建业务对象?

  • 问题描述 自发现服务(discovery_service)启动后,hwdiscovery 已派发对象组,但业务对象未被创建。

  • 一句话答案 检查 register_object_types() 是否正确注册、MC_OBJECT 的 class_name 是否与派发数据匹配。

  • 根因说明 可能原因:register_object_types() 未调用业务对象的 metadata() 方法、MC_OBJECT 中声明的类名与 hwdiscovery 派发的 class_name 不匹配、allowed_owners() 限制了来源但实际来源不在列表中、get_binary_objects_owner 配置不正确。

  • 解决方案

    1. 确认 register_object_types() 中已调用所有业务对象的 metadata()
    2. 核对 MC_OBJECT 的类名(第二个参数)与 hwdiscovery 派发的 class_name 一致
    3. 检查 allowed_owners() 返回值的空或包含正确的来源
    4. 确认 get_binary_objects_owner 配置的组件名与 hwdiscovery 注册的一致
  • 规避方案 使用 busctl monitor 监听 ObjectGroup 信号,确认 hwdiscovery 确实在发送对象组。

  • 适用版本 openUBMC 1.0.0 以上

Q5:如何新增一个自定义异常类型?

  • 问题描述 需要定义组件特定的异常类型以便更好地分类和处理错误。

  • 一句话答案 继承 mc::exception 并注册到异常工厂。

  • 根因说明 框架提供统一的异常处理机制,自定义异常需要遵循框架的异常层次结构,提供动态拷贝和重新抛出的能力。

  • 解决方案

    1. 定义自定义异常类继承 mc::exception
    2. 提供自定义的 exception_code 枚举值
    3. 实现 dynamic_copy_exception()dynamic_rethrow_exception() 方法
    4. 在程序初始化时调用 exception_factory::instance().register_exception<T>() 注册
    5. 使用 MC_THROW(my_exception, "消息") 抛出异常
  • 规避方案 如果不需要在异常工厂中动态创建异常,可以直接抛出而不注册。

  • 适用版本 openUBMC 1.0.0 以上