提交 60964c76 编写于 作者: O openeuler-ci-bot 提交者: Gitee

!4 vmtop:Show kvm exit items and add document to project

Merge pull request !4 from nocjj/master
# vmtop
#### Description
A tool for collecting and analyzing data of virtual machine
#### Software Architecture
Software architecture description
#### Installation
1. xxxx
2. xxxx
3. xxxx
#### Instructions
1. xxxx
2. xxxx
3. xxxx
#### Contribution
1. Fork the repository
2. Create Feat_xxx branch
3. Commit your code
4. Create Pull Request
#### Gitee Feature
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
4. The most valuable open source project [GVP](https://gitee.com/gvp)
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
# vmtop
#### 介绍
A tool for collecting and analyzing data of virtual machine
#### 软件架构
软件架构说明
#### 安装教程
1. xxxx
2. xxxx
3. xxxx
#### 使用说明
1. xxxx
2. xxxx
3. xxxx
#### 参与贡献
1. Fork 本仓库
2. 新建 Feat_xxx 分支
3. 提交代码
4. 新建 Pull Request
#### 码云特技
1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md
2. 码云官方博客 [blog.gitee.com](https://blog.gitee.com)
3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解码云上的优秀开源项目
4. [GVP](https://gitee.com/gvp) 全称是码云最有价值开源项目,是码云综合评定出的优秀开源项目
5. 码云官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help)
6. 码云封面人物是一档用来展示码云会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
A tool for collecting and analyzing data of virtual machine.
### Description
vmtop is a user-mode tool running on the host, which can dynamically view the resource usage of virtual machines in real time, include CPU usage, memory usage, vcpu kvm exits. It brings great convenience to virtualization problems and performance optimization, and is a pratical tool that intergrates multi-party info to monitor virtual machines.
### Building
This project uses ncurses, ncurses-devel, autoconf, libvirt, libvirt-devel. Go to check them out if you do not have them locally installed.
The simple steps to make vmtop are:
```sh
1. aclocal
2. autoconf
3. autoheader
4. automake --add-missing
5. ./configure
6. make
```
And then you will find vmtop in the source code dictionary.
### Introductions
#### usage
Run directly from the commandline.
```sh
vmtop [-option]
```
#### start option
- d: Set delay time between two display
- H: Show threads
- n: Set display times, default no limit
- b: Display in text mode to save result in file
#### shortcut key
- H: Open thread mode or close thread mode
- q: Quit from vmtop
- f: Show fields filter
### Contribution
#### Git
1. Fork the repository
2. Create Feat_xxx branch
3. Commit your code
4. Create Pull Request
#### Style
It is much better to be consistent with the exiting files. For new files:
- C: use kernel code style
- others: keep same with exiting files
#### Patches
Use 'git format-patch' to format patched and use 'git send-email' to send mail to [@openeuler](virt@openeuler.org) mailing list. And if it is a patch set, it is much better to use '--cover-letter' option when you format patches, so that we can understand what the pacthset does.
### Maintainers
[@zhanghailiang](https://gitee.com/zhanghailiang_luckyh)
[@nocjj](https://gitee.com/nocjj)
### License
[Mulan](License/LICENSE)
......@@ -10,6 +10,7 @@ AC_CONFIG_HEADERS([config.h])
AC_PROG_CC
AC_PROG_RANLIB
AM_INIT_AUTOMAKE([subdir-objects])
CC="$CC -std=gnu11"
# Checks for libraries.
AC_CHECK_LIB(ncurses, initscr, [], [echo "missing ncurses support!";exit -1])
......@@ -26,6 +27,18 @@ AC_TYPE_PID_T
AC_FUNC_MALLOC
AC_CHECK_FUNCS([memset realpath strstr strtoul])
# AC_ARGS_ENABLES
AC_ARG_ENABLE([debug],
[AS_HELP_STRING([--enable-debug], [debug program(default is disable)])],
[CFLAGS="-g -O0"],
[CFLAGS="-g -O2"]
)
AC_ARG_ENABLE([secure-build],
[AS_HELP_STRING([--enable-secure-build], [build project with secure flags])],
[CFLAGS="${CFLAGS} -Wl,-z,relro,-z,now -fPIE -fPIC -pie -Werror -Wall -ftrapv -fstack-protector-all -D_FORTIFY_SOURCE=2 -O2"],[]
)
AC_CONFIG_FILES([Makefile
src/Makefile])
AC_OUTPUT
1. Explanations of terms in project.
1) field
Member of task that is being to show in screen
===========================================
DID VMname PID %CPU S P
3 cjj2 64716 0.0 S 23
|_ qemu-kvm 64716 0.0 S 23
|_ qemu-kvm 64734 0.0 S 13
===========================================
"DID", "VMname" are field names, and "3" is the DID field of task,
"cjj2" is VMname field of task.
2) delay time
time unit 1s, time interval between two display, and is also time interval
for obtaining data.
2. What does items vmtop display means?
DID: virtual machine id in libvirt
VM/task-name: name of virtual machine or threads
PID: pid of qemu or threads
%CPU: CPU usage of task
EXTxxx: kvm exit times
P: physical cpu that task runs
S: task state, include: R, S, D, T, Z, X
%ST: cpu time vcpu wait pcpu while it is running other vcpu
%GUE: cpu time in guest
%HYP: cpu time in hypervisor
3. What archs are supported?
Currently, only aarch64 is supported.
4. Where to send bug report?
You may send your bug report to virt@openeuler.org or open an issue in
https://gitee.com/openeuler/vmtop.
1.How to add a display item for vmtop?
Of course, the display item data get should have been realized. Then, you can add related display item to vmtop display through the following steps:
1) Add a enum item in fields_type(field.h).
Take 'S' display item as example:
Add "FD_STATE" enum in fields_type
--------------------------------
enum fields_type {
FD_DID,
...
FD_STATE,
FD_END
};
--------------------------------
2) Add display setting in fields(field.c):
--------------------------------
FID fields[] = {
/* name . flag . align */
{"DID", FIELDS_DISPLAY, 5 },
...
{"S", FIELDS_DISPLAY, 5 }
};
--------------------------------
3) Add relate print case in print_domain_field(vmtop.c):
--------------------------------
static void print_domain_field(struct domain *dom, int field)
{
int i = field;
switch (i) {
case FD_VMNAME: {
| printw("%*.*s", fields[i].align, fields[i].align - 2, dom->vmname);
| break;
}
...
case FD_STATE: {
| printw("%*c", fields[i].align, dom->state);
| break;
}
...
}
--------------------------------
4) Make and run.
1. Disk io status
Show disk io status for each disk of each virtual machine.
2. Set io status
Show net io status for each net card of each virtual machine.
3. Sort key
Add short cut key to choose which field to sort by.
4. Introducing multithreads
Now, vmtop use delay time to set time interval between two data acquisitions.
......@@ -5,5 +5,7 @@ libdomain_a_SOURCES= \
domain.h \
proc.c \
proc.h \
vcpu_stat.c \
vcpu_stat.h \
utils.c \
utils.h
......@@ -18,6 +18,7 @@
#include "domain.h"
#include "utils.h"
#include "proc.h"
#include "vcpu_stat.h"
#define VAR_RUN_QEMU_PATH "/var/run/libvirt/qemu"
#define PID_STRING_MAX 12
......@@ -63,6 +64,9 @@ static void copy_domains(struct domain_list *now, struct domain_list *pre)
{
clear_domains(pre);
pre->num = now->num;
if (pre->num <= 0) {
return;
}
pre->domains = malloc(sizeof(struct domain) * pre->num);
if (pre->domains == NULL) {
pre->num = 0;
......@@ -70,6 +74,9 @@ static void copy_domains(struct domain_list *now, struct domain_list *pre)
}
memcpy(pre->domains, now->domains, sizeof(struct domain) * pre->num);
for (int i = 0; i < pre->num; i++) {
if (pre->domains[i].nlwp <= 0) {
continue;
}
pre->domains[i].threads = malloc(sizeof(struct domain) *
pre->domains[i].nlwp);
if (pre->domains[i].threads == NULL) {
......@@ -116,7 +123,7 @@ static int get_id_from_cgroup(pid_t pid)
char *tmp = NULL;
int id = -1;
if (snprintf(path, CGROUP_PATH_SIZE, "/proc/%lu/cgroup", pid) < 0) {
if (snprintf(path, CGROUP_PATH_SIZE, "/proc/%u/cgroup", pid) < 0) {
return id;
}
fp = fopen(path, "r");
......@@ -149,13 +156,16 @@ static int get_child_pid(struct domain *dom)
char *end = NULL;
int i = 0;
if (snprintf(path, TASK_STRING_SIZE, "/proc/%lu/task", dom->pid) < 0) {
if (snprintf(path, TASK_STRING_SIZE, "/proc/%u/task", dom->pid) < 0) {
return -1;
}
dirptr = opendir(path);
if (dirptr == NULL) {
return -1;
}
if (dom->nlwp <= 0) {
return -1;
}
dom->threads = (struct domain *)malloc(sizeof(struct domain) * dom->nlwp);
if (dom->threads == NULL) {
closedir(dirptr);
......@@ -181,6 +191,10 @@ static int get_child_pid(struct domain *dom)
get_proc_comm(&(dom->threads[i])) < 0) {
continue;
}
if (strstr(dom->threads[i].vmname, "CPU") != NULL
&& get_vcpu_stat(&(dom->threads[i])) > 0) {
dom->threads[i].type = ISVCPU;
}
i++;
}
closedir(dirptr);
......@@ -271,6 +285,10 @@ static void refresh_threads(struct domain *dom, struct domain *old_dom)
continue;
}
refresh_delta_stat(&(dom->threads[i]), old_thread);
if (dom->threads[i].type == ISVCPU) {
refresh_delta_vcpu_stat(&(dom->threads[i]), old_thread);
sum_vcpu_stat(dom, &(dom->threads[i]));
}
}
}
......@@ -295,3 +313,14 @@ int refresh_domains(struct domain_list *now, struct domain_list *pre)
return num;
}
int get_task_num(struct domain_list *list)
{
int sum = 0;
for (int i = 0; i < list->num; i++) {
sum += list->domains[i].nlwp;
}
sum += list->num;
return sum;
}
......@@ -23,4 +23,5 @@ struct domain_list {
int refresh_domains(struct domain_list *now, struct domain_list *pre);
void init_domains(struct domain_list *list);
int get_task_num(struct domain_list *list);
#endif
......@@ -23,10 +23,35 @@ const char *filter_help = ""
FID fields[] = {
/* name . flag . align */
{"DID", FIELDS_DISPLAY, 5 },
{"VMname", FIELDS_DISPLAY, 14 },
{"PID", FIELDS_DISPLAY, 8 },
{"%CPU", FIELDS_DISPLAY, 6 },
{"S", FIELDS_DISPLAY, 5 },
{"P", FIELDS_DISPLAY, 5 }
{"DID", FIELDS_DISPLAY, 5 },
{"VM/task-name", FIELDS_DISPLAY, 14 },
{"PID", FIELDS_DISPLAY, 8 },
{"%CPU", FIELDS_DISPLAY, 6 },
{"EXThvc", FIELDS_DISPLAY, 10 },
{"EXTwfe", FIELDS_DISPLAY, 10 },
{"EXTwfi", FIELDS_DISPLAY, 10 },
{"EXTmmioU", FIELDS_DISPLAY, 10 },
{"EXTmmioK", FIELDS_DISPLAY, 10 },
{"EXTfp", FIELDS_DISPLAY, 10 },
{"EXTirq", FIELDS_DISPLAY, 10 },
{"EXTsys64", FIELDS_DISPLAY, 10 },
{"EXTmabt", FIELDS_DISPLAY, 10 },
{"EXTsum", FIELDS_DISPLAY, 10 },
{"S", FIELDS_DISPLAY, 5 },
{"P", FIELDS_DISPLAY, 5 },
{"%ST", FIELDS_DISPLAY, 6 },
{"%GUE", FIELDS_DISPLAY, 6 },
{"%HYP", FIELDS_DISPLAY, 6 }
};
int get_show_field_num(void)
{
int sum = 0;
for (int i = 0; i < FD_END; i++) {
if (fields[i].display_flag == 1) {
sum++;
}
}
return sum;
}
......@@ -21,8 +21,21 @@ enum fields_type {
FD_VMNAME,
FD_PID,
FD_CPU,
FD_EXTHVC,
FD_EXTWFE,
FD_EXTWFI,
FD_EXTMMIOU,
FD_EXTMMIOK,
FD_EXTFP,
FD_EXTIRQ,
FD_EXTSYS64,
FD_EXTMABT,
FD_EXTSUM,
FD_STATE,
FD_P,
FD_ST,
FD_GUE,
FD_HYP,
FD_END
};
......@@ -36,4 +49,5 @@ extern FID fields[];
extern const char *summary_text;
extern const char *filter_help;
int get_show_field_num(void);
#endif
......@@ -19,8 +19,8 @@
#define STAT_PATH_SIZE 40
struct file_item proc_stab[] = {
#define GDF(f) (void *)GET_NAME(f), (void *)DELTA_NAME(f)
#define GF(f) (void *)GET_NAME(f), NULL
#define GDF(f) (void *)GET_NAME(f), (void *)DELTA_NAME(f), NULL
#define GF(f) (void *)GET_NAME(f), NULL, NULL
{"%c", GF(state)},
{"%d", GF(ppid)},
{"%d", GF(pgrd)},
......@@ -49,13 +49,13 @@ struct file_item proc_stab[] = {
{"%lu", GF(start_stack)},
{"%lu", GF(kstk_esp)},
{"%lu", GF(kstk_eip)},
{"%*s", NULL, NULL}, /* discard signal */
{"%*s", NULL, NULL}, /* discard blocked */
{"%*s", NULL, NULL}, /* discard sigignore */
{"%*s", NULL, NULL}, /* discard sigcatch */
{"%*s", NULL, NULL, NULL}, /* discard signal */
{"%*s", NULL, NULL, NULL}, /* discard blocked */
{"%*s", NULL, NULL, NULL}, /* discard sigignore */
{"%*s", NULL, NULL, NULL}, /* discard sigcatch */
{"%lu", GF(wchan)},
{"%*u", NULL, NULL}, /* dsicard nswap */
{"%*u", NULL, NULL}, /* discard cnswap */
{"%*u", NULL, NULL, NULL}, /* dsicard nswap */
{"%*u", NULL, NULL, NULL}, /* discard cnswap */
{"%d", GF(exit_signal)},
{"%d", GF(processor)},
{"%lu", GF(rtprio)},
......@@ -76,11 +76,11 @@ int get_proc_stat(struct domain *dom)
int i = 0;
if (dom->type == ISDOMAIN) {
if (snprintf(path, STAT_PATH_SIZE, "/proc/%lu/stat", dom->pid) < 0) {
if (snprintf(path, STAT_PATH_SIZE, "/proc/%u/stat", dom->pid) < 0) {
return -1;
}
} else {
if (snprintf(path, STAT_PATH_SIZE, "/proc/%lu/task/%lu/stat",
if (snprintf(path, STAT_PATH_SIZE, "/proc/%u/task/%u/stat",
dom->ppid, dom->pid) < 0) {
return -1;
}
......@@ -117,11 +117,13 @@ int get_proc_comm(struct domain *dom)
char path[STAT_PATH_SIZE];
int len;
if (snprintf(path, STAT_PATH_SIZE, "/proc/%lu/comm", dom->pid) < 0) {
if (snprintf(path, STAT_PATH_SIZE, "/proc/%u/comm", dom->pid) < 0) {
return -1;
}
len = read_file(dom->vmname, DOMAIN_NAME_MAX, path);
dom->vmname[len - 1] = '\0';
if (len > 1) {
dom->vmname[len - 1] = '\0';
}
return len;
}
......@@ -45,14 +45,24 @@ typedef unsigned long long u64;
new->DELTA_VALUE(v) = new->v - old->v; \
}
#define SUM_NAME(v) domain_sum_ ## v
#define SUM_FUN(v) \
static inline void SUM_NAME(v)(struct domain *dom, \
struct domain *thread) \
{ \
dom->DELTA_VALUE(v) += thread->DELTA_VALUE(v); \
}
#define GET_DELTA_FUN(v) \
GET_VALUE(v) \
DELTA_FUN(v)
DELTA_FUN(v) \
SUM_FUN(v)
struct file_item {
const char *format;
void *(*get_fun)(void *);
void (*delta_fun)(void *, void *);
void (*sum_fun)(void *, void *);
};
struct domain {
......@@ -98,6 +108,34 @@ struct domain {
cutime,
cstime,
start_time;
/* vcpu_stat items */
u64
DFX_VALUE(hvc_exit_stat),
DFX_VALUE(wfe_exit_stat),
DFX_VALUE(wfi_exit_stat),
DFX_VALUE(mmio_exit_user),
DFX_VALUE(mmio_exit_kernel),
DFX_VALUE(exits),
DFX_VALUE(fp_asimd_exit_stat),
DFX_VALUE(irq_exit_stat),
DFX_VALUE(sys64_exit_stat),
DFX_VALUE(mabt_exit_stat),
DFX_VALUE(fail_entry_exit_stat),
DFX_VALUE(internal_error_exit_stat),
DFX_VALUE(unknown_ec_exit_stat),
DFX_VALUE(cp15_32_exit_stat),
DFX_VALUE(cp15_64_exit_stat),
DFX_VALUE(cp14_mr_exit_stat),
DFX_VALUE(cp14_ls_exit_stat),
DFX_VALUE(cp14_64_exit_stat),
DFX_VALUE(smc_exit_stat),
DFX_VALUE(sve_exit_stat),
DFX_VALUE(debug_exit_stat),
DFX_VALUE(steal),
st_max,
DFX_VALUE(vcpu_utime),
DFX_VALUE(vcpu_stime),
DFX_VALUE(gtime);
struct domain *threads;
};
......
/******************************************************************************
* Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
* vmtop licensed under the Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
* PURPOSE.
* See the Mulan PSL v2 for more details.
* Description: get kvm exit data from vcpu_stat
********************************************************************************/
#include <stdio.h>
#include <string.h>
#include "type.h"
#include "vcpu_stat.h"
#define PID_STRING_SIZE 20
#define KVM_VCPU_STAT_PATH "/sys/kernel/debug/kvm/vcpu_stat"
struct file_item vcpu_stat_stab[] = {
#define GDF(f) (void *)GET_NAME(f), (void *)DELTA_NAME(f), (void *)SUM_NAME(f)
#define GF(f) (void *)GET_NAME(f), NULL, NULL
{"%*u", NULL, NULL, NULL},
{"%llu", GDF(hvc_exit_stat)},
{"%llu", GDF(wfe_exit_stat)},
{"%llu", GDF(wfi_exit_stat)},
{"%llu", GDF(mmio_exit_user)},
{"%llu", GDF(mmio_exit_kernel)},
{"%llu", GDF(exits)},
{"%llu", GDF(fp_asimd_exit_stat)},
{"%llu", GDF(irq_exit_stat)},
{"%llu", GDF(sys64_exit_stat)},
{"%llu", GDF(mabt_exit_stat)},
{"%llu", GDF(fail_entry_exit_stat)},
{"%llu", GDF(internal_error_exit_stat)},
{"%llu", GDF(unknown_ec_exit_stat)},
{"%llu", GDF(cp15_32_exit_stat)},
{"%llu", GDF(cp15_64_exit_stat)},
{"%llu", GDF(cp14_mr_exit_stat)},
{"%llu", GDF(cp14_ls_exit_stat)},
{"%llu", GDF(cp14_64_exit_stat)},
{"%llu", GDF(smc_exit_stat)},
{"%llu", GDF(sve_exit_stat)},
{"%llu", GDF(debug_exit_stat)},
{"%llu", GDF(steal)},
{"%llu", GF(st_max)},
{"%llu", GDF(vcpu_utime)},
{"%llu", GDF(vcpu_stime)},
{"%llu", GDF(gtime)}
#undef GF
#undef GDF
};
const int vcpu_stat_size = sizeof(vcpu_stat_stab) / sizeof(struct file_item);
int get_vcpu_stat(struct domain *dom)
{
char buf[BUF_SIZE];
char pid[PID_STRING_SIZE];
FILE *fp = NULL;
if (snprintf(pid, PID_STRING_SIZE, "%u", dom->pid) < 0) {
return -1;
}
fp = fopen(KVM_VCPU_STAT_PATH, "r");
if (!fp) {
return -1;
}
while (fgets(buf, BUF_SIZE - 1, fp)) {
char *p = NULL;
char *p_next = NULL;
int i = 0;
if (strstr(buf, pid) == NULL) {
continue;
}
for (p = strtok_r(buf, " \t\r\n", &p_next); p && i < vcpu_stat_size;
p = strtok_r(NULL, " \t\r\n", &p_next)) {
if (vcpu_stat_stab[i].get_fun) {
sscanf(p, vcpu_stat_stab[i].format,
(*vcpu_stat_stab[i].get_fun)(dom));
}
i++;
}
break;
}
fclose(fp);
return 1;
}
/*
* get delta value of kvm exit times over time
*/
void refresh_delta_vcpu_stat(struct domain *new, struct domain *old)
{
if (!new || !old) {
return;
}
for (int i = 0; i < vcpu_stat_size; i++) {
if (vcpu_stat_stab[i].delta_fun) {
(*vcpu_stat_stab[i].delta_fun)(new, old);
}
}
}
/*
* get sum of kvm exit from threads of virtla machine
*/
void sum_vcpu_stat(struct domain *dom, struct domain *thread)
{
if (!dom || !thread) {
return;
}
for (int i = 0; i < vcpu_stat_size; i++) {
if (vcpu_stat_stab[i].sum_fun) {
(*vcpu_stat_stab[i].sum_fun)(dom, thread);
}
}
}
/******************************************************************************
* Copyright (c) Huawei Technologies Co., Ltd. 2020-2020. All rights reserved.
* vmtop licensed under the Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR
* PURPOSE.
* See the Mulan PSL v2 for more details.
********************************************************************************/
#ifndef SRC_VCPU_STAT_H
#define SRC_VCPU_STAT_H
/* vcpu_stat items get fun */
GET_VALUE(pid)
GET_DELTA_FUN(hvc_exit_stat)
GET_DELTA_FUN(wfe_exit_stat)
GET_DELTA_FUN(wfi_exit_stat)
GET_DELTA_FUN(mmio_exit_user)
GET_DELTA_FUN(mmio_exit_kernel)
GET_DELTA_FUN(exits)
GET_DELTA_FUN(fp_asimd_exit_stat)
GET_DELTA_FUN(irq_exit_stat)
GET_DELTA_FUN(sys64_exit_stat)
GET_DELTA_FUN(mabt_exit_stat)
GET_DELTA_FUN(fail_entry_exit_stat)
GET_DELTA_FUN(internal_error_exit_stat)
GET_DELTA_FUN(unknown_ec_exit_stat)
GET_DELTA_FUN(cp15_32_exit_stat)
GET_DELTA_FUN(cp15_64_exit_stat)
GET_DELTA_FUN(cp14_mr_exit_stat)
GET_DELTA_FUN(cp14_ls_exit_stat)
GET_DELTA_FUN(cp14_64_exit_stat)
GET_DELTA_FUN(smc_exit_stat)
GET_DELTA_FUN(sve_exit_stat)
GET_DELTA_FUN(debug_exit_stat)
GET_DELTA_FUN(steal)
GET_VALUE(st_max)
GET_DELTA_FUN(vcpu_utime)
GET_DELTA_FUN(vcpu_stime)
GET_DELTA_FUN(gtime)
int get_vcpu_stat(struct domain *dom);
void refresh_delta_vcpu_stat(struct domain *new, struct domain *old);
void sum_vcpu_stat(struct domain *dom, struct domain *thread);
#endif
......@@ -22,12 +22,25 @@
#include "../config.h"
#define TIME_STR_MAX 40
#define TERM_MODE 0
#define TEXT_MODE 1
#define FLUSH_SCR() if (scr_mode == TERM_MODE) refresh(); else fflush(stdout)
int delay_time;
int quit_flag;
int scr_row_size; /* screen height, and will refresh in every loop */
int scr_col_size; /* screen width, and will refresh in every loop */
int showd_task; /* the num that has been showd, include task be hidden */
int begin_task;
int begin_field;
unsigned int thread_mode; /* decide whether to show threads */
int display_loop;
int scr_mode;
struct domain_list scr_cur;
struct domain_list scr_pre;
static int (*print_scr)(const char *fmt, ...);
static void init_screen(void)
{
initscr(); /* init to curses mode */
......@@ -35,14 +48,29 @@ static void init_screen(void)
noecho(); /* disable key echo while getch */
cbreak(); /* disable line buffer */
curs_set(0); /* set curse no display */
}
static void init_parameter(void)
{
init_domains(&scr_cur);
init_domains(&scr_pre);
begin_task = 1;
begin_field = 1;
thread_mode = 0; /* default not to show threads */
quit_flag = 0;
delay_time = 1; /* default delay 1s between display */
display_loop = -1;
scr_mode = TERM_MODE;
quit_flag = 0;
delay_time = 1; /* default delay 1s between display */
scr_row_size = 1024; /* defualt size row */
scr_col_size = 1024; /* default size col */
}
static void parse_args(int argc, char *argv[])
{
int opt;
char *arg_ops = "d:";
char *arg_ops = "Hd:n:b";
while ((opt = getopt(argc, argv, arg_ops)) != -1) {
switch (opt) {
case 'd': {
......@@ -52,6 +80,21 @@ static void parse_args(int argc, char *argv[])
}
break;
}
case 'H': {
thread_mode = 1;
break;
}
case 'n': {
display_loop = atoi(optarg);
if (display_loop == 0) {
display_loop = -1;
}
break;
}
case 'b': {
scr_mode = TEXT_MODE;
break;
}
default:
break;
}
......@@ -68,20 +111,28 @@ static void summary(void)
return;
}
printw(summary_text, time_buf, PACKAGE_VERSION, scr_cur.num);
print_scr(summary_text, time_buf, PACKAGE_VERSION, scr_cur.num);
return;
}
static void show_header(void)
{
int showd_width = 0;
int showd_field = 0;
attron(A_REVERSE);
for (int i = 0; i < FD_END; i++) {
if (fields[i].display_flag == 1) {
printw("%*s", fields[i].align, fields[i].name);
if (fields[i].display_flag == 1 &&
(i == FD_DID || i == FD_VMNAME || ++showd_field >= begin_field)) {
showd_width += fields[i].align;
if (showd_width >= scr_col_size) {
break;
}
print_scr("%*s", fields[i].align, fields[i].name);
}
}
attroff(A_REVERSE);
printw("\n");
print_scr("\n");
FLUSH_SCR();
}
/*
......@@ -93,34 +144,94 @@ static void print_domain_field(struct domain *dom, int field)
switch (i) {
case FD_VMNAME: {
printw("%*.*s", fields[i].align, fields[i].align - 2, dom->vmname);
print_scr("%*.*s", fields[i].align, fields[i].align - 2, dom->vmname);
break;
}
case FD_DID: {
if (dom->type == ISDOMAIN) {
printw("%*d", fields[i].align, dom->domain_id);
print_scr("%*d", fields[i].align, dom->domain_id);
} else {
printw("%*s", fields[i].align, "|_");
print_scr("%*s", fields[i].align, "|_");
}
break;
}
case FD_PID: {
printw("%*lu", fields[i].align, dom->pid);
print_scr("%*lu", fields[i].align, dom->pid);
break;
}
case FD_CPU: {
u64 cpu_jeffies = dom->DELTA_VALUE(utime) + dom->DELTA_VALUE(stime);
double usage = (double)cpu_jeffies * 100 /
sysconf(_SC_CLK_TCK) / delay_time;
printw("%*.1f", fields[i].align, usage);
print_scr("%*.1f", fields[i].align, usage);
break;
}
/* kvm exit fields show */
case FD_EXTHVC: {
print_scr("%*llu", fields[i].align, dom->DELTA_VALUE(hvc_exit_stat));
break;
}
case FD_EXTWFE: {
print_scr("%*llu", fields[i].align, dom->DELTA_VALUE(wfe_exit_stat));
break;
}
case FD_EXTWFI: {
print_scr("%*llu", fields[i].align, dom->DELTA_VALUE(wfi_exit_stat));
break;
}
case FD_EXTMMIOU: {
print_scr("%*llu", fields[i].align, dom->DELTA_VALUE(mmio_exit_user));
break;
}
case FD_EXTMMIOK: {
print_scr("%*llu", fields[i].align,
dom->DELTA_VALUE(mmio_exit_kernel));
break;
}
case FD_EXTFP: {
print_scr("%*llu", fields[i].align,
dom->DELTA_VALUE(fp_asimd_exit_stat));
break;
}
case FD_EXTIRQ: {
print_scr("%*llu", fields[i].align, dom->DELTA_VALUE(irq_exit_stat));
break;
}
case FD_EXTSYS64: {
print_scr("%*llu", fields[i].align, dom->DELTA_VALUE(sys64_exit_stat));
break;
}
case FD_EXTMABT: {
print_scr("%*llu", fields[i].align, dom->DELTA_VALUE(mabt_exit_stat));
break;
}
case FD_EXTSUM: {
print_scr("%*llu", fields[i].align, dom->DELTA_VALUE(exits));
break;
}
case FD_STATE: {
printw("%*c", fields[i].align, dom->state);
print_scr("%*c", fields[i].align, dom->state);
break;
}
case FD_P: {
printw("%*d", fields[i].align, dom->processor);
print_scr("%*d", fields[i].align, dom->processor);
break;
}
case FD_ST: {
print_scr("%*.1f", fields[i].align,
(double)dom->DELTA_VALUE(steal) / 1000000.0f / delay_time);
break;
}
case FD_GUE: {
print_scr("%*.1f", fields[i].align,
(double)dom->DELTA_VALUE(gtime) / 1000000.0f / delay_time);
break;
}
case FD_HYP: {
u64 hyp_time = dom->DELTA_VALUE(vcpu_utime) - dom->DELTA_VALUE(gtime) +
dom->DELTA_VALUE(vcpu_stime);
print_scr("%*.1f", fields[i].align,
(double)hyp_time / 1000000.0f / delay_time);
break;
}
default:
......@@ -129,31 +240,57 @@ static void print_domain_field(struct domain *dom, int field)
return;
}
static void show_task(struct domain *task)
{
int showd_width = 0; /* make show width do not beyond screen */
int showd_field = 0;
showd_task++;
if (showd_task < begin_task ||
showd_task - begin_task > scr_row_size - 4) {
return;
}
clrtoeol();
FLUSH_SCR(); /* clear line before display */
for (int i = 0; i < FD_END; i++) {
if (fields[i].display_flag == 1 &&
(i == FD_DID || i == FD_VMNAME || ++showd_field >= begin_field)) {
showd_width += fields[i].align;
if (showd_width >= scr_col_size) {
break;
}
print_domain_field(task, i);
}
}
FLUSH_SCR();
print_scr("\n");
}
static void show_domains_threads(struct domain *dom)
{
if (!dom) {
return;
}
for (int i = 0; i < dom->nlwp; i++) {
struct domain *thread = &(dom->threads[i]);
if (thread == NULL) {
continue;
}
for (int j = 0; j < FD_END; j++) {
print_domain_field(thread, j);
}
printw("\n");
show_task(thread);
}
}
static void show_domains(struct domain_list *list)
{
showd_task = 0;
for (int i = 0; i < list->num; i++) {
struct domain *dom = &(list->domains[i]);
for (int j = 0; j < FD_END; j++) {
print_domain_field(dom, j);
show_task(dom);
if (thread_mode == 1) {
show_domains_threads(dom);
}
printw("\n");
show_domains_threads(dom);
}
clrtobot(); /* clear to bottom to avoid image residue */
FLUSH_SCR();
}
/*
......@@ -163,7 +300,7 @@ static void print_field(int high_light)
{
int x = 3; /* display x local */
int y = 4; /* display y local */
int attr_flag;
unsigned int attr_flag;
for (int i = 0; i < FD_END; i++) {
attr_flag = A_NORMAL;
......@@ -240,6 +377,42 @@ static void parse_keys(int key)
quit_flag = !quit_flag;
break;
}
case KEY_UP: {
int task_num = thread_mode ? get_task_num(&scr_cur) : scr_cur.num;
begin_task++;
if (begin_task > task_num) {
begin_task = task_num;
}
break;
}
case KEY_DOWN: {
begin_task--;
if (begin_task < 1) {
begin_task = 1;
}
break;
}
case KEY_LEFT: {
begin_field--;
if (begin_field < 1) {
begin_field = 1;
}
break;
}
case KEY_RIGHT: {
int field_num = get_show_field_num();
begin_field++;
if (begin_field >= field_num) {
begin_field = field_num - 1;
}
break;
}
case 'H': {
thread_mode = !thread_mode;
begin_task = 1; /* mode change, so show from first task */
break;
}
default:
break;
}
......@@ -250,33 +423,49 @@ int main(int argc, char *argv[])
{
int key;
init_parameter();
parse_args(argc, argv);
init_screen();
quit_flag = 0;
delay_time = 1; /* default delay 1s between display*/
if (scr_mode == TERM_MODE) {
print_scr = printw;
init_screen();
} else {
print_scr = printf;
}
do {
if (scr_mode == TERM_MODE) {
getmaxyx(stdscr, scr_row_size, scr_col_size);
move(0, 0);
}
refresh_domains(&scr_cur, &scr_pre);
/* display frame make */
move(0, 0);
/* frame make */
summary();
EMPTY_LINE();
print_scr("\n");
show_header();
show_domains(&scr_cur);
/*
* set getch wait for delay time
* if timeout retutn ERR and continue
*/
halfdelay(delay_time * 10);
key = getch();
if (key != ERR) {
parse_keys(key);
clear();
if (scr_mode == TERM_MODE) {
/*
* set getch wait for delay time
* if timeout return ERR and continue
*/
halfdelay(delay_time * 10);
key = getch();
if (key != ERR) {
parse_keys(key);
clear();
}
} else {
usleep(delay_time * 1000000); /* wait delay time in text mode */
}
} while (!quit_flag);
endwin(); /* quit from curses mode */
FLUSH_SCR();
if (display_loop > 0) {
display_loop--;
}
} while (!quit_flag && display_loop);
if (scr_mode == TERM_MODE) {
endwin(); /* quit from curses mode */
}
return 0;
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册