2 * This file is part of the Palacios Virtual Machine Monitor developed
3 * by the V3VEE Project with funding from the United States National
4 * Science Foundation and the Department of Energy.
6 * The V3VEE Project is a joint project between Northwestern University
7 * and the University of New Mexico. You can find out more at
10 * Copyright (c) 2008, Jack Lange <jarusl@cs.northwestern.edu>
11 * Copyright (c) 2008, The V3VEE Project <http://www.v3vee.org>
12 * All rights reserved.
14 * Author: Jack Lange <jarusl@cs.northwestern.edu>
16 * This is free software. You are permitted to use,
17 * redistribute, and modify it as specified in the file "V3VEE_LICENSE".
20 #include <palacios/vmm_types.h>
21 #include <palacios/vmm_telemetry.h>
22 #include <palacios/svm_handler.h>
23 #include <palacios/vmm_rbtree.h>
24 #include <palacios/vmm_sprintf.h>
27 #ifdef V3_CONFIG_TELEMETRY_GRANULARITY
28 #define DEFAULT_GRANULARITY V3_CONFIG_TELEMETRY_GRANULARITY
30 #define DEFAULT_GRANULARITY 50000
37 void (*telemetry_fn)(struct v3_vm_info * vm, void * private_data, char * hdr);
40 struct list_head cb_node;
47 uint64_t handler_time;
49 struct rb_node tree_node;
53 static int free_callback(struct v3_vm_info * vm, struct telemetry_cb * cb);
54 static int free_exit(struct guest_info * core, struct exit_event * event);
57 void v3_init_telemetry(struct v3_vm_info * vm) {
58 struct v3_telemetry_state * telemetry = &(vm->telemetry);
60 telemetry->invoke_cnt = 0;
61 telemetry->granularity = DEFAULT_GRANULARITY;
63 telemetry->prev_tsc = 0;
65 INIT_LIST_HEAD(&(telemetry->cb_list));
68 void v3_deinit_telemetry(struct v3_vm_info * vm) {
69 struct telemetry_cb * cb = NULL;
70 struct telemetry_cb * tmp = NULL;
72 list_for_each_entry_safe(cb, tmp, &(vm->telemetry.cb_list), cb_node) {
73 free_callback(vm, cb);
78 void v3_init_core_telemetry(struct guest_info * core) {
79 struct v3_core_telemetry * telemetry = &(core->core_telem);
81 telemetry->exit_cnt = 0;
82 telemetry->vmm_start_tsc = 0;
84 telemetry->vm_telem = &(core->vm_info->telemetry);
86 telemetry->exit_root.rb_node = NULL;
89 void v3_deinit_core_telemetry(struct guest_info * core) {
90 struct rb_node * node = v3_rb_first(&(core->core_telem.exit_root));
91 struct exit_event * evt = NULL;
94 evt = rb_entry(node, struct exit_event, tree_node);
95 node = v3_rb_next(node);
103 static inline struct exit_event * __insert_event(struct guest_info * info,
104 struct exit_event * evt) {
105 struct rb_node ** p = &(info->core_telem.exit_root.rb_node);
106 struct rb_node * parent = NULL;
107 struct exit_event * tmp_evt = NULL;
111 tmp_evt = rb_entry(parent, struct exit_event, tree_node);
113 if (evt->exit_code < tmp_evt->exit_code) {
115 } else if (evt->exit_code > tmp_evt->exit_code) {
121 rb_link_node(&(evt->tree_node), parent, p);
126 static inline struct exit_event * insert_event(struct guest_info * info,
127 struct exit_event * evt) {
128 struct exit_event * ret;
130 if ((ret = __insert_event(info, evt))) {
134 v3_rb_insert_color(&(evt->tree_node), &(info->core_telem.exit_root));
140 static struct exit_event * get_exit(struct guest_info * info, uint_t exit_code) {
141 struct rb_node * n = info->core_telem.exit_root.rb_node;
142 struct exit_event * evt = NULL;
145 evt = rb_entry(n, struct exit_event, tree_node);
147 if (exit_code < evt->exit_code) {
149 } else if (exit_code > evt->exit_code) {
160 static inline struct exit_event * create_exit(uint_t exit_code) {
161 struct exit_event * evt = V3_Malloc(sizeof(struct exit_event));
163 evt->exit_code = exit_code;
165 evt->handler_time = 0;
172 static int free_exit(struct guest_info * core, struct exit_event * evt) {
173 v3_rb_erase(&(evt->tree_node), &(core->core_telem.exit_root));
179 void v3_telemetry_start_exit(struct guest_info * info) {
180 rdtscll(info->core_telem.vmm_start_tsc);
184 void v3_telemetry_end_exit(struct guest_info * info, uint_t exit_code) {
185 struct v3_core_telemetry * telemetry = &(info->core_telem);
186 struct exit_event * evt = NULL;
187 uint64_t end_tsc = 0;
191 evt = get_exit(info, exit_code);
194 evt = create_exit(exit_code);
195 insert_event(info, evt);
198 evt->handler_time += end_tsc - telemetry->vmm_start_tsc;
201 telemetry->exit_cnt++;
205 // check if the exit count has expired
206 if ((telemetry->exit_cnt % telemetry->vm_telem->granularity) == 0) {
207 v3_print_telemetry(info->vm_info, info);
214 void v3_add_telemetry_cb(struct v3_vm_info * vm,
215 void (*telemetry_fn)(struct v3_vm_info * vm, void * private_data, char * hdr),
216 void * private_data) {
217 struct v3_telemetry_state * telemetry = &(vm->telemetry);
218 struct telemetry_cb * cb = (struct telemetry_cb *)V3_Malloc(sizeof(struct telemetry_cb));
220 cb->private_data = private_data;
221 cb->telemetry_fn = telemetry_fn;
223 list_add(&(cb->cb_node), &(telemetry->cb_list));
228 static int free_callback(struct v3_vm_info * vm, struct telemetry_cb * cb) {
229 list_del(&(cb->cb_node));
236 static void telemetry_header(struct v3_vm_info *vm, char *hdr_buf, int len)
238 struct v3_telemetry_state * telemetry = &(vm->telemetry);
239 snprintf(hdr_buf, len, "telem.%d>", telemetry->invoke_cnt);
242 static void print_telemetry_start(struct v3_vm_info *vm, char *hdr_buf)
244 struct v3_telemetry_state * telemetry = &(vm->telemetry);
245 uint64_t invoke_tsc = 0;
247 V3_Print("%stelemetry window tsc cnt: %d\n", hdr_buf, (uint32_t)(invoke_tsc - telemetry->prev_tsc));
248 telemetry->prev_tsc = invoke_tsc;
251 static void print_telemetry_end(struct v3_vm_info *vm, char *hdr_buf)
253 V3_Print("%s Telemetry done\n", hdr_buf);
256 static void print_core_telemetry(struct guest_info * core, char *hdr_buf)
258 struct exit_event * evt = NULL;
259 struct rb_node * node = v3_rb_first(&(core->core_telem.exit_root));
261 V3_Print("Exit information for Core %d\n", core->vcpu_id);
264 V3_Print("No information yet for this core\n");
269 evt = rb_entry(node, struct exit_event, tree_node);
270 const char * code_str = vmexit_code_to_str(evt->exit_code);
272 V3_Print("%s%s:%sCnt=%u,%sAvg. Time=%u\n",
274 (strlen(code_str) > 13) ? "\t" : "\t\t",
276 (evt->cnt >= 100) ? "\t" : "\t\t",
277 (uint32_t)(evt->handler_time / evt->cnt));
278 } while ((node = v3_rb_next(node)));
282 void v3_print_core_telemetry(struct guest_info * core ) {
283 struct v3_vm_info *vm = core->vm_info;
284 struct v3_telemetry_state * telemetry = &(vm->telemetry);
287 telemetry_header(vm, hdr_buf, 32);
288 telemetry->invoke_cnt++; // XXX this increment isn't atomic and probably should be
290 print_telemetry_start(vm, hdr_buf);
291 print_core_telemetry(core, hdr_buf);
292 print_telemetry_end(vm, hdr_buf);
297 static void telemetry_callbacks(struct v3_vm_info * vm, char *hdr_buf)
299 struct v3_telemetry_state * telemetry = &(vm->telemetry);
300 // Registered callbacks
302 struct telemetry_cb * cb = NULL;
304 list_for_each_entry(cb, &(telemetry->cb_list), cb_node) {
305 cb->telemetry_fn(vm, cb->private_data, hdr_buf);
310 void v3_print_global_telemetry(struct v3_vm_info * vm) {
311 struct v3_telemetry_state * telemetry = &(vm->telemetry);
314 telemetry_header(vm, hdr_buf, 32);
315 telemetry->invoke_cnt++; // XXX this increment isn't atomic and probably should be
317 print_telemetry_start( vm, hdr_buf );
318 telemetry_callbacks( vm, hdr_buf );
319 print_telemetry_end( vm, hdr_buf );
322 void v3_print_telemetry(struct v3_vm_info * vm, struct guest_info * core )
324 struct v3_telemetry_state * telemetry = &(vm->telemetry);
327 telemetry_header(vm, hdr_buf, 32);
328 telemetry->invoke_cnt++; // XXX this increment isn't atomic and probably should be
330 print_telemetry_start(vm, hdr_buf);
331 print_core_telemetry(core, hdr_buf);
332 telemetry_callbacks(vm, hdr_buf);
333 print_telemetry_end(vm, hdr_buf);