# frameworks_system_utils
**Repository Path**: open-vela/frameworks_system_utils
## Basic Information
- **Project Name**: frameworks_system_utils
- **Description**: - frameworks_system_utils:该仓库包含了系统基础组件和服务,包括 kvdb、trace 和 uv 等。
- **Primary Language**: C
- **License**: Apache-2.0
- **Default Branch**: dev
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 17
- **Forks**: 3
- **Created**: 2024-11-29
- **Last Updated**: 2026-02-02
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# Utils
[[English](./README.md) | [中文](./README_zh-cn.md)]
## 项目概览
当前目录下包含的主要是framework当中提供的一些常用工具实现.
| 工具 | 工具简要描述 |
| -- | -- |
| `gdbus` | 对`D-Bus`接口封装, 方便操作`D-Bus` |
| `kvdb` | 基于本地数据库的`键值对`数据存取接口 |
| `log` | 提供和`Android`平台兼容的`Log API`接口
用于在`Vela`当中直接使用`Android` LOG API |
| `trace` | 提供用于用户空间程序的打点工具 |
## 项目描述
### 1. gdbus
`gdbus`模块是对`D-Bus`接口的进一步封装的API模块. 由于`D-Bus`接口使用比较复杂, 在实际模块使用时, 通过将`D-Bus`接口再次封装, 会方便使用. `gdbus`通过提供更简易操作的API接口,方便我们去操作`D-Bus`.
### 2. kvdb
#### 2.1 简要介绍
`kvdb`提供了一套本地数据库的读写接口, `API`设计参考`Android`的`properties`存取规范, 同时提供了命令行工具以方便本地快速调试.
`Vela`中的`kvdb`支持本地永久化存储以及跨核调用(分别需要Unix domain socket和rpmsg socket支持), 需要永久存储到文件的键值需要以`"persist."`开头.
`kvdb`底层实现包含了三种机制:
1. 基于开源的`UnQLite`数据库, 依赖于数据库;
2. 另外一种是基于`MTD CONFIG` (目前仅用于nor flash);
3. 最后一种基于`file`文件;
> `kvdb`一般接口说明: [frameworks/utils/include/kvdb.h](include/kvdb.h)
#### 2.2 `kvdb`的常见配置说明
| kvdb配置 | 说明 |
| -- | -- |
| CONFIG_KVDB_PRIORITY | KVDB任务优先级, 默认为系统默认值 |
| CONFIG_KVDB_STACKSIZE | KVDB栈空间分配,默认为系统默认值 |
| CONFIG_KVDB_SERVER | KVDB SERVER模式. 表示当前CPU是否为读写文件的主CPU, 为n则只调用其他CPU上的KVDB |
| CONFIG_KVDB_DIRECT | KVDB DIRECT模式: 在无需rpmsg socket的场景(无需跨核),可使用此模式
CONFIG_KVDB_DIRECT 与 CONFIG_KVDB_SERVER 两种模式只能二选一 |
| CONFIG_KVDB_COMMIT_INTERVAL | KVDB提交间隔 (秒), 默认为5
KVDB有内部缓存, 提交后才真正写入文件, 如果提交persist类型的kv后, `CONFIG_KVDB_COMMIT_INTERVAL`时间前就下电, 数据不会真正写入到`persist.db`文件中. `CONFIG_KVDB_COMMIT_INTERVAL`时间设置的越短, `kvdb`将内部缓存写入文件越频繁, 会一定程度上影响系统性能 |
| CONFIG_KVDB_SOURCE_PATH | KVDB默认值加载路径,默认为`"/etc/build.prop"`, 支持多个路径, 用`;`分隔即可. 每次开机启动会自动从该文件加载KV值 |
| CONFIG_KVDB_UNQLITE | 配置使用 unqlite database 存储 kv |
| CONFIG_KVDB_NVS | 配置使用 nvs 存储 kv |
| CONFIG_KVDB_FILE | 配置使用 file 存储 kv |
> `CONFIG_KVDB_UNQLITE`,`CONFIG_KVDB_NVS`, `CONFIG_KVDB_FILE` 三种数据存储的backend只能三选一
### 3. log
log模块本身是一个wrapper层,底层将Vela log系统进行了封装,封装成的API是和Android当中的log API一致, 当我们将Android应用或者框架移植到Vela当中, 不需要再提供自己的log对接,直接使用当前模块就可以了.
以下是log模块的结构:
```log
android log api
|
\|/
log wrapper
|
\|/
vela log impl
```
### 4. trace
这个模块当中包含的主要是用于用户空间程序的打点工具. 我们可以通过在用户程序当中手动插桩来实现打点分析.
trace当中提供的atrace工具主要是配合Vela系统提供的打点工具来使用的.
## 使用指南
### 1. gdbus
1. 打开`CONFIG_LIB_DBUS`选项
2. 实例化
gdbus为接口模块, 需要调用者实例化, 并在调用gdbus接口时, 传入实例化对象.
调用者需要创建一个结构体, 成员至少有`gdbus Connection`和`client`实例对象, 类似如下:
```cpp
typedef struct {
DBusConnection* connection;
GDBusClient* client;
GDBusProxy* dbus_proxy[USER_SUPPORT_PROXY_MAX];
bool client_ready;
} dbus_context;
dbus_context* ctx = malloc(sizeof(dbus_context));
//创建DbusConnection实例
ctx->connection = g_dbus_setup_private(DBUS_BUS_SYSTEM, NULL, NULL);
//设置Connection退出函数,否则dbus Connection退出时,会同步退出当前进程,添加后,则不会退出当前进程
g_dbus_set_disconnect_function(ctx->connection, system_dbus_disconnected, callback, NULL);
//设置当前dbus Connection的client名字
dbus_request_name(ctx->connection, client_name, &err);
ctx->client = g_dbus_client_new(ctx->connection, OFONO_SERVICE, OFONO_MANAGER_PATH);
//设置proxy的property的filter函数,
//ofono_interface_proxy_added 监听proxy添加,按需缓存需要使用的proxy实例,供后续操作这个proxy的属性和method
//ofono_interface_proxy_removed 监听proxy删除,同步清除缓存的proxy实例
//在object_filter函数中设置哪些proxy不需要读取property属性
//ofono_property_changed 监听proper属性的变化,按需缓存对应proxy的proper值。
g_dbus_client_set_proxy_handlers(dbus_client, ofono_interface_proxy_added,
ofono_interface_proxy_removed,
object_filter,
ofono_property_changed, tele);
//设置dbus client连接成功的回调函数on_dbus_client_ready
g_dbus_client_set_ready_watch(ctx->client, on_dbus_client_ready, cbd)
```
3. 退出释放
调用者进程退出时, 需要释放`dbus Connection`实例, 执行如下代码:
```cpp
g_dbus_client_unref(ctx->client);
dbus_connection_close(ctx->connection);
dbus_connection_unref(ctx->connection);
```
4. 调用接口
调用接口, 传入`voice manager proxy`实例, 发起dial请求:
```cpp
g_dbus_proxy_method_call(ctx->dbus_proxy[VOICE_MANAGER], "Dial", dial_setup,
dial_reply, param, dial_destory)
```
### 2. kvdb
`kvdb`本身有多种使用形式,我们可以直接在代码当中集成,也可以直接在`nsh`当中以命令行程序的方式来使用.
#### 2.1 直接在代码当中集成的示例
以下是针对`kvdb`当中提供的监控key/value变化的接口的demo, 针对简单和复杂场景,提供了2套API:
1. 简单场景: 只能监控一个`key`
```cpp
int main(void)
{
char newkey[PROPERTY_KEY_MAX];
char newvalue[PROPERTY_VALUE_MAX];
int ret = property_wait("tsetkey", newkey, newvalue, -1);
if (ret < 0)
{
printf("property_wait failed, ret=%d\n", ret);
goto out;
}
printf("the new key: %s\n", newkey);
printf("the new value: %s\n", newvalue);
out:
return ret;
}
```
2. 复杂场景: 支持poll,用户自由监控多个key
```cpp
int main(void)
{
struct pollfd fds[2];
char newkey[PROPERTY_KEY_MAX];
char newvalue[PROPERTY_VALUE_MAX];
int fd1 = property_monitor_open("monitorkey*");
int fd2 = property_monitor_open("testkey");
fds[0].fd = fd1;
fds[0].events = POLLIN;
fds[1].fd = fd2;
fds[1].events = POLLIN;
int ret= poll(fds, 2, -1);
if (ret <= 0)
goto out;
for (int i = 0; i < 2; i++)
{
if ((fds[i].revents & POLLIN) == 0)
continue;
ret = property_monitor_read(fds[i].fd, newkey, newvalue);
if (ret < 0)
goto out;
printf("the new key: %s\n", newkey);
printf("the new value: %s\n", newvalue);
}
out:
property_monitor_close(fd1);
property_monitor_close(fd2);
return ret;
}
```
#### 2.2 在nsh当中以命令行的形式来使用
KVDB提供了getprop和setprop两个命令行程序供用户使用,用户可以使用getprop和setprop方便地查看已存在的KV或是设置新的KV。
这两个命令行在使能KVDB后默认开启
getprop:打印出设置的property
- nsh> getprop:列出当前所有props
- nsh> getprop : 打印出对应的prop
setprop:设置或者删除property
- nsh> setprop : 删除对应的prop
- nsh> setprop : 保存:到数据库
下面是具体的使用示例:
```log
nsh> setprop name peter #添加名为name值为peter的键值对, 掉电消失
nsh> setprop persist.name1 peter1 #添加名为name1值为peter1的键值对, 掉电不消失
nsh> getprop #查看所有键值对
name: peter
nsh> setprop name #删除名为name的键值对
nsh> getprop name
```
### 3. log
1. 打开`CONFIG_ANDROID_LIBBASE`
2. 然后我们可以在程序当中直接使用标准的android log api来收集打印日志.
```cpp
#include
#define LOG_TAG "MyAppTag"
int main() {
// 自定义优先级打印 log
__android_log_print(ANDROID_LOG_INFO, LOG_TAG, "Formatted number: %d", 42);
// 使用宏打印 INFO 级别 log
ALOGI("ALOGI: A log message from my app.");
return 0;
}
```
### 4. trace
1. 打开`CONFIG_SCHED_INSTRUMENTATION_DUMP`和`CONFIG_ATRACE`选项
2. 在程序当中需要跟踪的地方添加上打点信息:
```cpp
// 使用是添加头文件,必须添加TAG
#define ATRACE_TAG ATRACE_TAG_ALWAYS
#include
int main(int argc, char *argv[])
{
// 对当前函数进行插桩
ATRACE_BEGIN("hello_main");
sleep(1);
ATRACE_INSTANT("printf");
printf("hello world!");
// 结束插桩
ATRACE_END();
return 0;
}
```
3. 使用trace dump工具查看打点输出的结果:
```log
hello-7 [0] 3.187400000: sched_wakeup_new: comm=hello pid=7 target_cpu=0
hello-7 [0] 3.187400000: tracing_mark_write: B|7|hello_main
hello-7 [0] 4.197700000: tracing_mark_write: I|7|printf
hello-7 [0] 4.187700000: tracing_mark_write: E|7|hello_main
```
另外就是 atrace 的输出结果也是可以直接使用 [perfetto](https://ui.perfetto.dev/) 工具以可视化的形式来查看 trace 的时序图的.