一,前言

今天继续分析主要的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 },
};

  1. 代码首先获取mask参数
case QEMU_OPTION_d:
 log_mask = optarg;
 break;
  1. 解析完参数后,将参数通过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;
}
  1. 关于传参-d

我想要打印qemu_log_function_name只要加-d func即可。由于打印的内容多,所以也可以打印到log文件,用-D qemulogfile

B,关于属性

  1. 最早属性创建函数是在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);

Qemu源码分析(7)--Apple的学习笔记_qemu

另外我研究下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申请好空间即可

Qemu源码分析(7)--Apple的学习笔记_qemu_02


  1. 为属性设置描述

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*

Qemu源码分析(7)--Apple的学习笔记_qemu_03

再回到最早,我添加了一些注释

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;
}
  1. 分析过如上关于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);
 }
}
  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面向对象的抽象结构体设计的好。





阿里云国内75折 回扣 微信号:monov8
阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6