/* Copyright (C) 2009 Red Hat, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, see . */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #define TAB_LEN 4 #define VALUE_TABS 7 #define INVALID_STAT_REF (~(uint32_t)0) verify(sizeof(SpiceStat) == 20 || sizeof(SpiceStat) == 24); static SpiceStatNode *reds_nodes = NULL; static uint64_t *values = NULL; static void print_stat_tree(int32_t node_index, int depth) { SpiceStatNode *node = &reds_nodes[node_index]; if ((node->flags & SPICE_STAT_NODE_MASK_SHOW) == SPICE_STAT_NODE_MASK_SHOW) { printf("%*s%s", depth * TAB_LEN, "", node->name); if (node->flags & SPICE_STAT_NODE_FLAG_VALUE) { printf(":%*s%"PRIu64" (%"PRIu64")\n", (int) ((VALUE_TABS - depth) * TAB_LEN - strlen(node->name) - 1), "", node->value, node->value - values[node_index]); values[node_index] = node->value; } else { printf("\n"); if (node->first_child_index != INVALID_STAT_REF) { print_stat_tree(node->first_child_index, depth + 1); } } } if (node->next_sibling_index != INVALID_STAT_REF) { print_stat_tree(node->next_sibling_index, depth); } } // look for a single file /dev/shm/spice.XXX and extract XXX pid static pid_t search_pid(void) { pid_t pid = 0; glob_t globbuf; if (glob("/dev/shm/spice.*", 0, NULL, &globbuf)) { return pid; } if (globbuf.gl_pathc == 1) { const char *p = strchr(globbuf.gl_pathv[0], '.'); if (p) { pid = atoi(p+1); } } globfree(&globbuf); return pid; } int main(int argc, char **argv) { char *shm_name; pid_t kvm_pid = 0; uint32_t num_of_nodes = 0; size_t shm_size; int shm_name_len; int ret = EXIT_FAILURE; int fd; struct stat st; unsigned header_size = sizeof(SpiceStat); SpiceStat *reds_stat = (SpiceStat *)MAP_FAILED; if (argc == 2) { kvm_pid = atoi(argv[1]); } else if (argc == 1) { kvm_pid = search_pid(); } if (argc > 2 || !kvm_pid) { fprintf(stderr, "usage: reds_stat [qemu_pid] (e.g. `pgrep qemu`)\n"); return ret; } shm_name_len = strlen(SPICE_STAT_SHM_NAME) + 64; if (!(shm_name = (char *)malloc(shm_name_len))) { perror("malloc"); return ret; } snprintf(shm_name, shm_name_len, SPICE_STAT_SHM_NAME, kvm_pid); if ((fd = shm_open(shm_name, O_RDONLY, 0444)) == -1) { perror("shm_open"); free(shm_name); return ret; } shm_size = sizeof(SpiceStat); reds_stat = (SpiceStat *)mmap(NULL, shm_size, PROT_READ, MAP_SHARED, fd, 0); if (fstat(fd, &st) == 0) { unsigned size = st.st_size % sizeof(SpiceStatNode); if (size == 20 || size == 24) { header_size = size; } } if (reds_stat == (SpiceStat *)MAP_FAILED) { perror("mmap"); goto error; } if (reds_stat->magic != SPICE_STAT_MAGIC) { fprintf(stderr, "bad magic %u\n", reds_stat->magic); goto error; } if (reds_stat->version != SPICE_STAT_VERSION) { fprintf(stderr, "bad version %u\n", reds_stat->version); goto error; } while (1) { if (system("clear") != 0) { printf("\n\n\n"); } printf("spice statistics\n\n"); if (num_of_nodes != reds_stat->num_of_nodes) { num_of_nodes = reds_stat->num_of_nodes; munmap(reds_stat, shm_size); shm_size = header_size + num_of_nodes * sizeof(SpiceStatNode); reds_stat = (SpiceStat *)mmap(NULL, shm_size, PROT_READ, MAP_SHARED, fd, 0); if (reds_stat == (SpiceStat *)MAP_FAILED) { perror("mmap"); goto error; } reds_nodes = (SpiceStatNode *)((char *) reds_stat + header_size); values = (uint64_t *)realloc(values, num_of_nodes * sizeof(uint64_t)); if (values == NULL) { perror("realloc"); goto error; } memset(values, 0, num_of_nodes * sizeof(uint64_t)); } print_stat_tree(reds_stat->root_index, 0); sleep(1); } ret = EXIT_SUCCESS; error: free(values); if (reds_stat != (SpiceStat *)MAP_FAILED) { munmap(reds_stat, shm_size); } shm_unlink(shm_name); free(shm_name); return ret; }