一,前言
今天继续分析主要的api,包括了print log和属性add及set,因为对于不同对象的操作,主要就是靠属性值设置及判断使用。
二,源码分析
A,关于qemu中自带的-d的log需要传入的参数
主要通过-d 然后传入的参数如下第2个成员,比如out_asmconst QEMULogItem
qemu_log_items[] = {
{ CPU_LOG_TB_OUT_ASM, "out_asm",
"show generated host assembly code for each compiled TB" },
{ CPU_LOG_TB_IN_ASM, "in_asm",
"show target assembly code for each compiled TB" },
{ CPU_LOG_TB_OP, "op",
"show micro ops for each compiled TB" },
{ CPU_LOG_TB_OP_OPT, "op_opt",
"show micro ops after optimization" },
{ CPU_LOG_TB_OP_IND, "op_ind",
"show micro ops before indirect lowering" },
{ CPU_LOG_INT, "int",
"show interrupts/exceptions in short format" },
{ CPU_LOG_EXEC, "exec",
"show trace before each executed TB (lots of logs)" },
{ CPU_LOG_TB_CPU, "cpu",
"show CPU registers before entering a TB (lots of logs)" },
{ CPU_LOG_MMU, "mmu",
"log MMU-related activities" },
{ CPU_LOG_PCALL, "pcall",
"x86 only: show protected mode far calls/returns/exceptions" },
{ CPU_LOG_RESET, "cpu_reset",
"show CPU state before CPU resets" },
{ LOG_UNIMP, "unimp",
"log unimplemented functionality" },
{ LOG_GUEST_ERROR, "guest_errors",
"log when the guest OS does something invalid (eg accessing a\n"
"non-existent register)" },
{ CPU_LOG_PAGE, "page",
"dump pages at beginning of user mode emulation" },
{ CPU_LOG_TB_NOCHAIN, "nochain",
"do not chain compiled TBs so that \"exec\" and \"cpu\" show\n"
"complete traces" },
#if defined(CONFIG_GNU_ARM_ECLIPSE)
{ LOG_FUNC, "func",
"log functions entry" },
{ LOG_MR, "mr",
"log trace messages for memory regions read/writes" },
#endif /* defined(CONFIG_GNU_ARM_ECLIPSE) */
{ 0, NULL, NULL },
};
- 代码首先获取mask参数
case QEMU_OPTION_d:
log_mask = optarg;
break;
- 解析完参数后,将参数通过qemu_str_to_log_mask函数获取mask值。
if (log_mask) {
int mask;
mask = qemu_str_to_log_mask(log_mask);
if (!mask) {
qemu_print_log_usage(stdout);
exit(1);
}
qemu_set_log(mask);
} else {
qemu_set_log(0);
}
主要的解析就是通过char **parts = g_strsplit(str, ",", 0);按逗号拆分为字符list。
for (tmp = parts; tmp && *tmp; tmp++) {
…
for (item = qemu_log_items; item->mask != 0; item++) {
if (g_str_equal(*tmp, item->name)) {
goto found;
}
}
goto error;
found:
mask |= item->mask;
}
- 关于传参-d
我想要打印qemu_log_function_name,只要加-d func即可。由于打印的内容多,所以也可以打印到log文件,用-D qemulogfile
B,关于属性
- 最早属性创建函数是在object_new的时候添加的。对应TypeImp中有instance_init则会调用,属性是GHashTable *说明一个object可以挂很多属性。
if (ti->instance_init) {
ti->instance_init(obj);
}
最后这些注册的函数中会存在object_property_add,添加属性类中的值。
object_property_add_str(obj, "image", machine_get_image, machine_set_image, NULL);
另外,我研究下opaqeu主要的作用,他的参数是void *opaque;如下图
object_property_add_str(obj, "image",machine_get_image, machine_set_image, NULL);
prop就是 StringProperty *prop = g_malloc0(sizeof(*prop));所以用了void*后就很灵活,只要malloc申请好空间即可。
- 为属性设置描述
object_property_set_description(obj, "image","Bare-bone image file",NULL);
比如上面的截图image已经创建完成了,然后为description赋值。
void object_property_set_description(Object *obj, const char *name,
const char *description, Error **errp)
{
ObjectProperty *op;
op = object_property_find(obj, name, errp);
if (!op) {
return;
}
g_free(op->description);
op->description = g_strdup(description);
}
关于object_property_add_child函数,其实就是添加属性,而且可以看到object_property_add的倒数第二个参数是child(object*类型的),其实就是opaque。
而resolve的函数是object_resolve_child_property,其实就是返回opaque。
static Object *object_resolve_child_property(Object *parent, void *opaque, const gchar *part)
{
return opaque;
}
那么如下object_resolve_path_component返回的其实就是opaque,也就是child的object*。
再回到最早,我添加了一些注释
CortexMBoardState *cortexm_board_get(void)
{
if (board == NULL) {
board = container_get(object_get_root(), "/machine");
}
if (board == NULL) {
return NULL;
}
return CORTEXM_BOARD_STATE(board);
}
Object *object_get_root(void)
{
//是首个节点地址
if (!root) {
root = object_new("container"); //root赋值为container
}
return root;
}
/*从main函数开始最早调用*object_get_root的是如下,也就是创建了一个machine part到属性表,挂载在container节点后面。
object_property_add_child(object_get_root(), "machine",
OBJECT(current_machine), &error_abort);
所以才有之后的container_get函数去找这个OBJECT(current_machine)的object指针。*/
Object *container_get(Object *root, const char *path)
{
Object *obj, *child;
gchar **parts;
int i;
parts = g_strsplit(path, "/", 0);
assert(parts != NULL && parts[0] != NULL && !parts[0][0]);
obj = root; // obj设置为root
for (i = 1; parts[i] != NULL; i++, obj = child) {
// 在root中的属性中找parts名字一样的child object。
child = object_resolve_path_component(obj, parts[i]);
//若没有找到这个parts,那么在root中添加一个节点container
if (!child) { child = object_new("container");
object_property_add_child(obj, parts[i], child, NULL);
}
}
g_strfreev(parts);
return obj;
}
Object *object_resolve_path_component(Object *parent, const gchar *part)
{
ObjectProperty *prop = object_property_find(parent, part, NULL);
if (prop == NULL) {
return NULL;
}
//属性找到后就调用注册函数返回child object对象指针
if (prop->resolve) {
return prop->resolve(parent, prop->opaque, part);
} else {
return NULL;
}
}
ObjectProperty *object_property_find(Object *obj, const char *name,
Error **errp)
{
ObjectProperty *prop;
ObjectClass *klass = object_get_class(obj);
// 先在Klass的父类的属性表中找name是否有匹配的
prop = object_class_property_find(klass, name, NULL);
if (prop) {
return prop;
}
// 就在当前object中的属性表中找name
prop = g_hash_table_lookup(obj->properties, name);
if (prop) {
return prop;
}
error_setg(errp, "Property '.%s' not found", name);
return NULL;
}
- 分析过如上关于object属性,再看下图247行就很容易理解了
其实就是调用了object_property_add_child,刚刚都已经分析过了。
void cm_object_property_add_child(Object *obj, const char *node_name,
Object *child)
{
Error *err = NULL;
object_property_add_child(obj, node_name, child, &err);
if (err) {
error_report("Adding child %s for %s failed: %s.", node_name,
object_get_typename(obj), error_get_pretty(err));
exit(1);
}
}
- 接着看set属性值
接着会看到
cm_object_property_set_int(mcu, 8000000, "hse-freq-hz"); // 8.0 MHz
Set了hse-freq-hz,那么之前一定add过这个属性,所以能找到stm32_mcu_instance_init_callback函数中有add这个属性,最后一个参数等于设置这个属性的默认值,也就是为opaque成员赋值。
cm_object_property_add_uint32(obj, "hse-freq-hz", &state->hse_freq_hz);
这些instance_init都是object_New的TypeImp初始化的时候创建的,也就是我这块虚拟board的父类对象创建过程中创建的。
5.cm_object_realize(mcu);我本来以为也就是设置一个mcu对象的属性
void cm_object_realize(Object *obj)
{
Error *err = NULL;
object_property_set_bool(obj, true, "realized", &err);
if (err) {
error_report("Realization of object %s failed: %s",
object_get_typename(obj), error_get_pretty(err));
exit(1);
}
}
结果可以看到调用了很多注册的函数实现了虚拟mcu创建的功能。
cortexm_mcu_realize_callback
cm_device_parent_realize
stm32_mcu_realize_callback
cm_device_by_name_realize
stm32_mcus_realize_callback
device_set_realized
property_set_bool
object_property_set
object_property_set_qobject
object_property_set_bool
cm_object_realize
stm32f4_discovery_board_init_callback
qemu_main
main
主要就是有摄像动态的钩子函数在起作用,导致了千变万化的效果。比如 property_set_bool中的prop->set(obj, value, errp);
比较关键是device_set_realized和cm_device_parent_realize主要是填充CortexMState对象,包括Flash,中断和内存,最后加载客户端elf。
三,小结
本次主要学习了*void的成员的多样化设计思路,另外就是发现钩子函数的妙用,我平时基本仅用一层钩子函数,所以功能单一,但是多种钩子函数组合后,变化的效果就很多了,主要还是qemu面向对象的抽象结构体设计的好。