当前目录下包含的主要是framework当中提供的一些常用工具实现.
| 工具 | 工具简要描述 |
|---|---|
gdbus |
对D-Bus接口封装, 方便操作D-Bus |
kvdb |
基于本地数据库的键值对数据存取接口 |
log |
提供和Android平台兼容的Log API接口用于在 Vela当中直接使用Android LOG API |
trace |
提供用于用户空间程序的打点工具 |
gdbus模块是对D-Bus接口的进一步封装的API模块. 由于D-Bus接口使用比较复杂, 在实际模块使用时, 通过将D-Bus接口再次封装, 会方便使用. gdbus通过提供更简易操作的API接口,方便我们去操作D-Bus.
kvdb提供了一套本地数据库的读写接口, API设计参考Android的properties存取规范, 同时提供了命令行工具以方便本地快速调试.
Vela中的kvdb支持本地永久化存储以及跨核调用(分别需要Unix domain socket和rpmsg socket支持), 需要永久存储到文件的键值需要以"persist."开头.
kvdb底层实现包含了三种机制:
- 基于开源的
UnQLite数据库, 依赖于数据库; - 另外一种是基于
MTD CONFIG(目前仅用于nor flash); - 最后一种基于
file文件;
kvdb一般接口说明: frameworks/utils/include/kvdb.h
| 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只能三选一
log模块本身是一个wrapper层,底层将Vela log系统进行了封装,封装成的API是和Android当中的log API一致, 当我们将Android应用或者框架移植到Vela当中, 不需要再提供自己的log对接,直接使用当前模块就可以了. 以下是log模块的结构:
android log api
|
\|/
log wrapper
|
\|/
vela log impl
这个模块当中包含的主要是用于用户空间程序的打点工具. 我们可以通过在用户程序当中手动插桩来实现打点分析. trace当中提供的atrace工具主要是配合Vela系统提供的打点工具来使用的.
-
打开
CONFIG_LIB_DBUS选项 -
实例化
gdbus为接口模块, 需要调用者实例化, 并在调用gdbus接口时, 传入实例化对象.
调用者需要创建一个结构体, 成员至少有gdbus Connection和client实例对象, 类似如下:
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)- 退出释放
调用者进程退出时, 需要释放dbus Connection实例, 执行如下代码:
g_dbus_client_unref(ctx->client);
dbus_connection_close(ctx->connection);
dbus_connection_unref(ctx->connection);- 调用接口
调用接口, 传入voice manager proxy实例, 发起dial请求:
g_dbus_proxy_method_call(ctx->dbus_proxy[VOICE_MANAGER], "Dial", dial_setup,
dial_reply, param, dial_destory)kvdb本身有多种使用形式,我们可以直接在代码当中集成,也可以直接在nsh当中以命令行程序的方式来使用.
以下是针对kvdb当中提供的监控key/value变化的接口的demo, 针对简单和复杂场景,提供了2套API:
- 简单场景: 只能监控一个
key
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;
}- 复杂场景: 支持poll,用户自由监控多个key
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;
}KVDB提供了getprop和setprop两个命令行程序供用户使用,用户可以使用getprop和setprop方便地查看已存在的KV或是设置新的KV。 这两个命令行在使能KVDB后默认开启 getprop:打印出设置的property
- nsh> getprop:列出当前所有props
- nsh> getprop : 打印出对应的prop
setprop:设置或者删除property
- nsh> setprop : 删除对应的prop
- nsh> setprop : 保存:到数据库
下面是具体的使用示例:
nsh> setprop name peter #添加名为name值为peter的键值对, 掉电消失
nsh> setprop persist.name1 peter1 #添加名为name1值为peter1的键值对, 掉电不消失
nsh> getprop #查看所有键值对
name: peter
nsh> setprop name #删除名为name的键值对
nsh> getprop name
- 打开
CONFIG_ANDROID_LIBBASE - 然后我们可以在程序当中直接使用标准的android log api来收集打印日志.
#include <log/log.h>
#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;
}-
打开
CONFIG_SCHED_INSTRUMENTATION_DUMP和CONFIG_ATRACE选项 -
在程序当中需要跟踪的地方添加上打点信息:
// 使用是添加头文件,必须添加TAG
#define ATRACE_TAG ATRACE_TAG_ALWAYS
#include <cutils/trace.h>
int main(int argc, char *argv[])
{
// 对当前函数进行插桩
ATRACE_BEGIN("hello_main");
sleep(1);
ATRACE_INSTANT("printf");
printf("hello world!");
// 结束插桩
ATRACE_END();
return 0;
}- 使用trace dump工具查看打点输出的结果:
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 工具以可视化的形式来查看 trace 的时序图的.