I. Use GDB to analyze qemu code
Using GDB not only can debug the code well, but also can be used to dynamically analyze the code. To debug qemu using GDB, you need to make some preparations:
1. During qemu compilation, add-enable-Debug to the parameters when the configure script is executed.
2, download a streamlined image-linux-0.2.img from the qemu official website. The linux-0.2.img is only 8 Mb in size and contains some common shell commands after startup for qemu testing.
$wget http://wiki.qemu.org/download/linux-0.2.img.bz2$bzip2 -d ./linux-0.2.img.bz2
3. Start GDB to debug qemu:
gdb --args qemu-system-x86_64 -enable-kvm -m 4096 -smp 4 linux-0.2.img
-SMP indicates the number of processors.
Ii. Data Structure Used for Parameter Parsing
The main function simulated by the qemu system is located in the Vl. c file, whether it is a qemu-system-x86_64 or a qemu-system-ppc64, it is executed from the main function in Vl. C. The following describes some data structures involved in the main function.
Qemu linked list
The linked list of qemu is defined in the include/qemu/queue. h file. There are four types:
- Singly-linked list.
- List: A two-way linked list. In addition to the header node, each node simultaneously points to the forward node and the next node.
- Simple queue: A simple queue is similar to a single-chain table, but a header pointing to the end of the linked list is added, when you insert a node, you can not only insert it into the header or a node like a single-chain table, but also insert it to the end of the linked list.
- Tail queue: similar to a simple queue, but the nodes direct to each other in two directions.
The usage of various linked lists is not described here. The usage of qemu linked lists (lists) is described only through the definition of notifierlist. The displaystate struct defined at the beginning of the main function uses notifierelist, And the notifierlist uses the linked list.
A. Table header and node Definition
Qlist_head is required to define the header. The definition is as follows:
86 #define QLIST_HEAD(name, type) 87 struct name { 88 struct type *lh_first; /* first element */ 89 }
Notifierlist uses qlist_head to define the header:
27 typedef struct NotifierList 28 { 29 QLIST_HEAD(, Notifier) notifiers; 30 } NotifierList;
To define a node, you must use qlist_entry, which is defined as follows:
94 #define QLIST_ENTRY(type) 95 struct { 96 struct type *le_next; /* next element */ 97 struct type **le_prev; /* address of previous next element */ 98 }
Notifier nodes are defined as follows:
21 struct Notifier 22 { 23 void (*notify)(Notifier *notifier, void *data); 24 QLIST_ENTRY(Notifier) node; 25 };
B. initialize the header.
Qlist_init is used to initialize the header:
103 #define QLIST_INIT(head) do { 104 (head)->lh_first = NULL; 105 } while (/*CONSTCOND*/0)
Initialize notifierlist as follows:
19 void notifier_list_init(NotifierList *list) 20 { 21 QLIST_INIT(&list->notifiers); 22 }
C. Insert a node into the header
Insert the node to the header using qlist_insert_head:
122 #define QLIST_INSERT_HEAD(head, elm, field) do { 123 if (((elm)->field.le_next = (head)->lh_first) != NULL) 124 (head)->lh_first->field.le_prev = &(elm)->field.le_next;125 (head)->lh_first = (elm); 126 (elm)->field.le_prev = &(head)->lh_first; 127 } while (/*CONSTCOND*/0)
Insert notifier to notifierlist:
24 void notifier_list_add(NotifierList *list, Notifier *notifier) 25 { 26 QLIST_INSERT_HEAD(&list->notifiers, notifier, node); 27 }
D. Traverse nodes
The traversal node uses qlist_foreach or qlist_foreach_safe. qlist_foreach_safe is used to prevent the node from being deleted during the traversal process. As a result, le_next is released and the traversal is interrupted.
147 #define QLIST_FOREACH(var, head, field) 148 for ((var) = ((head)->lh_first); 149 (var); 150 (var) = ((var)->field.le_next))151 152 #define QLIST_FOREACH_SAFE(var, head, field, next_var) 153 for ((var) = ((head)->lh_first); 154 (var) && ((next_var) = ((var)->field.le_next), 1); 155 (var) = (next_var))
Notifierlist uses qlist_foreach_safe when executing all callback functions:
34 void notifier_list_notify(NotifierList *list, void *data) 35 { 36 Notifier *notifier, *next; 37 38 QLIST_FOREACH_SAFE(notifier, &list->notifiers, node, next) { 39 notifier->notify(notifier, data); 40 } 41 }
Error and qerror
To facilitate error processing, qemu defines two data structures: Error and qerror. Error is defined in qobject/qerror. C:
101 struct Error102 {103 char *msg;104 ErrorClass err_class;105 };
Contains the error message string and enumeration type error categories. There are several error categories:
139 typedef enum ErrorClass 140 { 141 ERROR_CLASS_GENERIC_ERROR = 0, 142 ERROR_CLASS_COMMAND_NOT_FOUND = 1, 143 ERROR_CLASS_DEVICE_ENCRYPTED = 2, 144 ERROR_CLASS_DEVICE_NOT_ACTIVE = 3, 145 ERROR_CLASS_DEVICE_NOT_FOUND = 4, 146 ERROR_CLASS_K_V_M_MISSING_CAP = 5, 147 ERROR_CLASS_MAX = 6, 148 } ErrorClass;
Qemu defines several functions in util/error. C to Operate Error:
Error_set // allocate space for Error Based on the given errorclass and formatted string and assign error_set_errno // in addition to the error_set function, append the error message of the specified errno to error_copy after the formatted string // copy error error_is_set // judge whether error has been assigned and set error_get_class // obtain error errorclass error_get_pretty // obtain Error msgerror_free // release space for error and MSG
In addition, qemu defines qerror to handle more detailed error information:
22 typedef struct QError { 23 QObject_HEAD; 24 Location loc; 25 char *err_msg; 26 ErrorClass err_class; 27 } QError;
Qerror can assign values to err_msg and err_class through a series of macros:
39 #define QERR_ADD_CLIENT_FAILED 40 ERROR_CLASS_GENERIC_ERROR, "Could not add client" 41 42 #define QERR_AMBIGUOUS_PATH 43 ERROR_CLASS_GENERIC_ERROR, "Path ‘%s‘ does not uniquely identify an object" 44 45 #define QERR_BAD_BUS_FOR_DEVICE 46 ERROR_CLASS_GENERIC_ERROR, "Device ‘%s‘ can‘t go on a %s bus" 47 48 #define QERR_BASE_NOT_FOUND 49 ERROR_CLASS_GENERIC_ERROR, "Base ‘%s‘ not found"...
Location records the error location, which is defined as follows:
20 typedef struct Location { 21 /* all members are private to qemu-error.c */ 22 enum { LOC_NONE, LOC_CMDLINE, LOC_FILE } kind; 23 int num; 24 const void *ptr; 25 struct Location *prev; 26 } Location;
Gmainloop
Qemu uses gmainloop in glib to achieve Io multiplexing. For details about gmainloop, refer to the implementation principle and code model of gmainloop in the blog. Since gmainloop is not the code of qemu, this article will not repeat it.
Iii. qemuoption, qemuopt, and qemu Parameter Parsing
Qemu defines qemuoption to indicate the options used to execute commands such as qemu-system-x86_64. The definition in Vl. C is as follows:
2123 typedef struct qemuoption {2124 const char * Name; // option name, for example,-device. The value of name is device2125 int flags; // flag, indicating whether the option contains a parameter, it can be 0 or has_arg (value: 0x0001) 2126 int index; // an enumerated value, such as-device, which is qemu_option_device2127 uint32_t arch_mask; // The option supports the architecture mask 2128} qemuoption;
A qemuoption array qemu_options is maintained in Vl. C to store all available options and assign values to the Array Using qemu-options-wrapper.h and qemu-options.def. The assignment statement is as follows:
2130 static const QEMUOption qemu_options[] = {2131 { "h", 0, QEMU_OPTION_h, QEMU_ARCH_ALL },2132 #define QEMU_OPTIONS_GENERATE_OPTIONS2133 #include "qemu-options-wrapper.h"2134 { NULL },2135 };
# Define qemu_options_generate_options select the qemu-options-wrapper.h operation, the qemu-options-wrapper.h can perform three operations:
Outputs: generate a list of enumerated values using the qemu-options.def, that is, the qemu_option_device qemu_options_generate_help: Generate help information using the qemu-options.def and output it to the standard output qemu_options_generate_options
You can expand the qemu-options-wrapper.h through the following method to view the results of the above operation, taking generating options as an example.
- Write # define qemu_options_generate_options In the first line of the qemu-options-wrapper.h.
- Execute Command
gcc -E -o options.txt qemu-options-wrapper.h
- Upload File options.txt.
After assigning values to the qemu_options array, qemu has a set of all available options. In Vl. C, A for loop of the main function starts parsing the command line according to this set. The for loop framework is roughly as follows:
1 for(;;) { 2 if (optind >= argc) 3 break; 4 if (argv[optind][0] != ‘-‘) { 5 hda_opts = drive_add(IF_DEFAULT, 0, argv[optind++], HD_OPTS); 6 } else { 7 const QEMUOption *popt; 8 9 popt = lookup_opt(argc, argv, &optarg, &optind); 10 if (!(popt->arch_mask & arch_type)) { 11 printf("Option %s not supported for this target\n", popt->name); 12 exit(1); 13 } 14 switch(popt->index) { 15 case QEMU_OPTION_M: 16 ...... 17 case QEMU_OPTION_hda: 18 ...... 19 case QEMU_OPTION_watchdog: 20 ...... 21 default: 22 os_parse_cmd_args(popt->index, optarg); 23 } 24 } 25 }
Qemu regards the string starting with '-' in argv as an option, and then calls the lookup_opt function to search for this option in the qemu_options array. If the value of flags in the found option is has_arg, lookup_opt also assigns the parameter string to optarg. After the options and parameters are found, qemu executes different branches based on the index enumerated values in the options.
For some switch-related options, the branch operation only assigns values to the corresponding flag bits, for example:
3712 case QEMU_OPTION_old_param:3713 old_param = 1;3714 break;
Some options do not have sub-options. When the branch is executed, the optarg value is directly handed over to the related variable:
3822 case QEMU_OPTION_qtest:3823 qtest_chrdev = optarg;3824 break;
Qemu processing is more complex for options with sub-options, such as "-drive if = none, id = drive-ID. It will call qemu_opts_parse to parse sub-options, such as Realtime options:
3852 case QEMU_OPTION_realtime:3853 opts = qemu_opts_parse(qemu_find_opts("realtime"), optarg, 0);3854 if (!opts) {3855 exit(1);3856 }3857 configure_realtime(opts);3858 break;
The parsing of sub-options involves four data structures: qemuopt, qemudesc, qemuopts, and qemuoptslist. Their relationships are shown in:
Qemuopt stores sub-options. Each qemuopt has a qemuoptdesc to describe the sub-options name, type, and help information. The two struct types are defined as follows:
32 struct qemuopt {33 const char * Name; // suboption name 34 const char * STR; // string value 35 36 const qemuoptdesc * DESC; 37 Union {38 bool Boolean; // Boolean value 39 uint64_t uint; // number or size 40} value; 41 42 qemuopts * opts; 43 qtailq_entry (qemuopt) next; 44 }; 95 typedef struct qemuoptdesc {96 const char * Name; 97 Enum qemuopttype; 98 const char * help; 99} qemuoptdesc;
The sub-options can be of the following types:
88 Enum qemuopttype {89 qemu_opt_string = 0, // string 90 bytes, // The value can be on or off 91 qemu_opt_number, // number 92 qemu_opt_size, // The size can be K, m, G, T, and other suffixes 93 };
Qemu maintains an array of qemuoptslist *, which is defined in util/qemu-config.c:
10 static QemuOptsList *vm_config_groups[32];
In the main function, qemu_add_opts writes various qemuoptslist into the array:
2944 qemu_add_opts(&qemu_drive_opts);2945 qemu_add_opts(&qemu_chardev_opts);2946 qemu_add_opts(&qemu_device_opts); 2947 qemu_add_opts(&qemu_netdev_opts);2948 qemu_add_opts(&qemu_net_opts);2949 qemu_add_opts(&qemu_rtc_opts);2950 qemu_add_opts(&qemu_global_opts);2951 qemu_add_opts(&qemu_mon_opts);2952 qemu_add_opts(&qemu_trace_opts);2953 qemu_add_opts(&qemu_option_rom_opts);2954 qemu_add_opts(&qemu_machine_opts);2955 qemu_add_opts(&qemu_smp_opts);2956 qemu_add_opts(&qemu_boot_opts);2957 qemu_add_opts(&qemu_sandbox_opts);2958 qemu_add_opts(&qemu_add_fd_opts);2959 qemu_add_opts(&qemu_object_opts);2960 qemu_add_opts(&qemu_tpmdev_opts);2961 qemu_add_opts(&qemu_realtime_opts);2962 qemu_add_opts(&qemu_msg_opts);
Each qemuoptslist stores all the small options supported by the large options, such as the definition of the-Realtime large option qemuoptslist:
507 static QemuOptsList qemu_realtime_opts = { 508 .name = "realtime", 509 .head = QTAILQ_HEAD_INITIALIZER(qemu_realtime_opts.head), 510 .desc = { 511 { 512 .name = "mlock", 513 .type = QEMU_OPT_BOOL, 514 }, 515 { /* end of list */ } 516 }, 517 };
-Realtime only supports one sub-option and the value is of the bool type, that is, it can only be on or off.
Before calling the parse resolution sub-option, qemu will call qemu_find_opts ("realtime"), find qemuoptslist * From qemu_add_opts, and pass it together with optarg to qemu_opts_parse for resolution. Qemu may use the same large option multiple times to specify multiple identical devices. In this case, you need to use IDs to differentiate them. The qemuopts struct represents all the sub-options under the same ID. The definition is as follows:
46 struct QemuOpts { 47 char *id; 48 QemuOptsList *list; 49 Location loc; 50 QTAILQ_HEAD(QemuOptHead, QemuOpt) head; 51 QTAILQ_ENTRY(QemuOpts) next; 52 };
List is the qemuopts linked list with different IDS under the same big option.