-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
tools: Add guest trace agent as a user tool
This patch adds a user tool, "trace agent" for sending trace data of a guest to a Host in low overhead. This agent has the following functions: - splice a page of ring-buffer to read_pipe without memory copying - splice the page from write_pipe to virtio-console without memory copying - write trace data to stdout by using -o option - controlled by start/stop orders from a Host Changes in v2: - Cleanup (change fprintf() to pr_err() and an include guard) Signed-off-by: Yoshihiro YUNOMAE <yoshihiro.yunomae.ez@hitachi.com> Acked-by: Amit Shah <amit.shah@redhat.com> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
- Loading branch information
Yoshihiro YUNOMAE
authored and
Rusty Russell
committed
Sep 28, 2012
1 parent
8ca84a5
commit 108fc82
Showing
6 changed files
with
806 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
CC = gcc | ||
CFLAGS = -O2 -Wall | ||
LFLAG = -lpthread | ||
|
||
all: trace-agent | ||
|
||
.c.o: | ||
$(CC) $(CFLAGS) $(LFLAG) -c $^ -o $@ | ||
|
||
trace-agent: trace-agent.o trace-agent-ctl.o trace-agent-rw.o | ||
$(CC) $(CFLAGS) $(LFLAG) -o $@ $^ | ||
|
||
clean: | ||
rm -f *.o trace-agent |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
Trace Agent for virtio-trace | ||
============================ | ||
|
||
Trace agent is a user tool for sending trace data of a guest to a Host in low | ||
overhead. Trace agent has the following functions: | ||
- splice a page of ring-buffer to read_pipe without memory copying | ||
- splice the page from write_pipe to virtio-console without memory copying | ||
- write trace data to stdout by using -o option | ||
- controlled by start/stop orders from a Host | ||
|
||
The trace agent operates as follows: | ||
1) Initialize all structures. | ||
2) Create a read/write thread per CPU. Each thread is bound to a CPU. | ||
The read/write threads hold it. | ||
3) A controller thread does poll() for a start order of a host. | ||
4) After the controller of the trace agent receives a start order from a host, | ||
the controller wake read/write threads. | ||
5) The read/write threads start to read trace data from ring-buffers and | ||
write the data to virtio-serial. | ||
6) If the controller receives a stop order from a host, the read/write threads | ||
stop to read trace data. | ||
|
||
|
||
Files | ||
===== | ||
|
||
README: this file | ||
Makefile: Makefile of trace agent for virtio-trace | ||
trace-agent.c: includes main function, sets up for operating trace agent | ||
trace-agent.h: includes all structures and some macros | ||
trace-agent-ctl.c: includes controller function for read/write threads | ||
trace-agent-rw.c: includes read/write threads function | ||
|
||
|
||
Setup | ||
===== | ||
|
||
To use this trace agent for virtio-trace, we need to prepare some virtio-serial | ||
I/Fs. | ||
|
||
1) Make FIFO in a host | ||
virtio-trace uses virtio-serial pipe as trace data paths as to the number | ||
of CPUs and a control path, so FIFO (named pipe) should be created as follows: | ||
# mkdir /tmp/virtio-trace/ | ||
# mkfifo /tmp/virtio-trace/trace-path-cpu{0,1,2,...,X}.{in,out} | ||
# mkfifo /tmp/virtio-trace/agent-ctl-path.{in,out} | ||
|
||
For example, if a guest use three CPUs, the names are | ||
trace-path-cpu{0,1,2}.{in.out} | ||
and | ||
agent-ctl-path.{in,out}. | ||
|
||
2) Set up of virtio-serial pipe in a host | ||
Add qemu option to use virtio-serial pipe. | ||
|
||
##virtio-serial device## | ||
-device virtio-serial-pci,id=virtio-serial0\ | ||
##control path## | ||
-chardev pipe,id=charchannel0,path=/tmp/virtio-trace/agent-ctl-path\ | ||
-device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,\ | ||
id=channel0,name=agent-ctl-path\ | ||
##data path## | ||
-chardev pipe,id=charchannel1,path=/tmp/virtio-trace/trace-path-cpu0\ | ||
-device virtserialport,bus=virtio-serial0.0,nr=2,chardev=charchannel0,\ | ||
id=channel1,name=trace-path-cpu0\ | ||
... | ||
|
||
If you manage guests with libvirt, add the following tags to domain XML files. | ||
Then, libvirt passes the same command option to qemu. | ||
|
||
<channel type='pipe'> | ||
<source path='/tmp/virtio-trace/agent-ctl-path'/> | ||
<target type='virtio' name='agent-ctl-path'/> | ||
<address type='virtio-serial' controller='0' bus='0' port='0'/> | ||
</channel> | ||
<channel type='pipe'> | ||
<source path='/tmp/virtio-trace/trace-path-cpu0'/> | ||
<target type='virtio' name='trace-path-cpu0'/> | ||
<address type='virtio-serial' controller='0' bus='0' port='1'/> | ||
</channel> | ||
... | ||
Here, chardev names are restricted to trace-path-cpuX and agent-ctl-path. For | ||
example, if a guest use three CPUs, chardev names should be trace-path-cpu0, | ||
trace-path-cpu1, trace-path-cpu2, and agent-ctl-path. | ||
|
||
3) Boot the guest | ||
You can find some chardev in /dev/virtio-ports/ in the guest. | ||
|
||
|
||
Run | ||
=== | ||
|
||
0) Build trace agent in a guest | ||
$ make | ||
|
||
1) Enable ftrace in the guest | ||
<Example> | ||
# echo 1 > /sys/kernel/debug/tracing/events/sched/enable | ||
|
||
2) Run trace agent in the guest | ||
This agent must be operated as root. | ||
# ./trace-agent | ||
read/write threads in the agent wait for start order from host. If you add -o | ||
option, trace data are output via stdout in the guest. | ||
|
||
3) Open FIFO in a host | ||
# cat /tmp/virtio-trace/trace-path-cpu0.out | ||
If a host does not open these, trace data get stuck in buffers of virtio. Then, | ||
the guest will stop by specification of chardev in QEMU. This blocking mode may | ||
be solved in the future. | ||
|
||
4) Start to read trace data by ordering from a host | ||
A host injects read start order to the guest via virtio-serial. | ||
# echo 1 > /tmp/virtio-trace/agent-ctl-path.in | ||
|
||
5) Stop to read trace data by ordering from a host | ||
A host injects read stop order to the guest via virtio-serial. | ||
# echo 0 > /tmp/virtio-trace/agent-ctl-path.in |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
/* | ||
* Controller of read/write threads for virtio-trace | ||
* | ||
* Copyright (C) 2012 Hitachi, Ltd. | ||
* Created by Yoshihiro Yunomae <yoshihiro.yunomae.ez@hitachi.com> | ||
* Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com> | ||
* | ||
* Licensed under GPL version 2 only. | ||
* | ||
*/ | ||
|
||
#define _GNU_SOURCE | ||
#include <fcntl.h> | ||
#include <poll.h> | ||
#include <signal.h> | ||
#include <stdio.h> | ||
#include <stdlib.h> | ||
#include <unistd.h> | ||
#include "trace-agent.h" | ||
|
||
#define HOST_MSG_SIZE 256 | ||
#define EVENT_WAIT_MSEC 100 | ||
|
||
static volatile sig_atomic_t global_signal_val; | ||
bool global_sig_receive; /* default false */ | ||
bool global_run_operation; /* default false*/ | ||
|
||
/* Handle SIGTERM/SIGINT/SIGQUIT to exit */ | ||
static void signal_handler(int sig) | ||
{ | ||
global_signal_val = sig; | ||
} | ||
|
||
int rw_ctl_init(const char *ctl_path) | ||
{ | ||
int ctl_fd; | ||
|
||
ctl_fd = open(ctl_path, O_RDONLY); | ||
if (ctl_fd == -1) { | ||
pr_err("Cannot open ctl_fd\n"); | ||
goto error; | ||
} | ||
|
||
return ctl_fd; | ||
|
||
error: | ||
exit(EXIT_FAILURE); | ||
} | ||
|
||
static int wait_order(int ctl_fd) | ||
{ | ||
struct pollfd poll_fd; | ||
int ret = 0; | ||
|
||
while (!global_sig_receive) { | ||
poll_fd.fd = ctl_fd; | ||
poll_fd.events = POLLIN; | ||
|
||
ret = poll(&poll_fd, 1, EVENT_WAIT_MSEC); | ||
|
||
if (global_signal_val) { | ||
global_sig_receive = true; | ||
pr_info("Receive interrupt %d\n", global_signal_val); | ||
|
||
/* Wakes rw-threads when they are sleeping */ | ||
if (!global_run_operation) | ||
pthread_cond_broadcast(&cond_wakeup); | ||
|
||
ret = -1; | ||
break; | ||
} | ||
|
||
if (ret < 0) { | ||
pr_err("Polling error\n"); | ||
goto error; | ||
} | ||
|
||
if (ret) | ||
break; | ||
}; | ||
|
||
return ret; | ||
|
||
error: | ||
exit(EXIT_FAILURE); | ||
} | ||
|
||
/* | ||
* contol read/write threads by handling global_run_operation | ||
*/ | ||
void *rw_ctl_loop(int ctl_fd) | ||
{ | ||
ssize_t rlen; | ||
char buf[HOST_MSG_SIZE]; | ||
int ret; | ||
|
||
/* Setup signal handlers */ | ||
signal(SIGTERM, signal_handler); | ||
signal(SIGINT, signal_handler); | ||
signal(SIGQUIT, signal_handler); | ||
|
||
while (!global_sig_receive) { | ||
|
||
ret = wait_order(ctl_fd); | ||
if (ret < 0) | ||
break; | ||
|
||
rlen = read(ctl_fd, buf, sizeof(buf)); | ||
if (rlen < 0) { | ||
pr_err("read data error in ctl thread\n"); | ||
goto error; | ||
} | ||
|
||
if (rlen == 2 && buf[0] == '1') { | ||
/* | ||
* If host writes '1' to a control path, | ||
* this controller wakes all read/write threads. | ||
*/ | ||
global_run_operation = true; | ||
pthread_cond_broadcast(&cond_wakeup); | ||
pr_debug("Wake up all read/write threads\n"); | ||
} else if (rlen == 2 && buf[0] == '0') { | ||
/* | ||
* If host writes '0' to a control path, read/write | ||
* threads will wait for notification from Host. | ||
*/ | ||
global_run_operation = false; | ||
pr_debug("Stop all read/write threads\n"); | ||
} else | ||
pr_info("Invalid host notification: %s\n", buf); | ||
} | ||
|
||
return NULL; | ||
|
||
error: | ||
exit(EXIT_FAILURE); | ||
} |
Oops, something went wrong.