Skip to content

Commit 99c69c7

Browse files
lyan3jren1
authored andcommitted
DM:tools: add acrntrace tool
acrntrace: is an debug tool running on SOS to capture trace data. Usage: 1) Start tracing Capture buffered trace data: # acrntrace or clear buffer before tracing start: # acrntrace -c A folder will be created to save the trace data files are under /tmp/acrntrace/, named with time string. Eg: /tmp/acrntrace/20171115-101605 2) Stop tracing # q <enter> Reviewed-by: Zhao Yakui <yakui.zhao@intel.com> Signed-off-by: Yan, Like <like.yan@intel.com> Acked-by: Eddie Dong <eddie.dong@intel.com>
1 parent c730806 commit 99c69c7

File tree

6 files changed

+866
-0
lines changed

6 files changed

+866
-0
lines changed

devicemodel/tools/acrntrace/Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
all:
2+
gcc -o acrntrace acrntrace.c sbuf.c -I. -lpthread
3+
4+
clean:
5+
rm acrntrace
Lines changed: 381 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,381 @@
1+
/*
2+
* Copyright (C) 2018 Intel Corporation. All rights reserved.
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions
6+
* are met:
7+
*
8+
* * Redistributions of source code must retain the above copyright
9+
* notice, this list of conditions and the following disclaimer.
10+
* * Redistributions in binary form must reproduce the above copyright
11+
* notice, this list of conditions and the following disclaimer in
12+
* the documentation and/or other materials provided with the
13+
* distribution.
14+
* * Neither the name of Intel Corporation nor the names of its
15+
* contributors may be used to endorse or promote products derived
16+
* from this software without specific prior written permission.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
*/
30+
31+
#include <stdio.h>
32+
#include <stdlib.h>
33+
#include <unistd.h>
34+
#include <fcntl.h>
35+
#include <sys/mman.h>
36+
#include <time.h>
37+
#include <signal.h>
38+
#include <errno.h>
39+
#include <pthread.h>
40+
#include <string.h>
41+
42+
#include "acrntrace.h"
43+
#include "trace_event.h"
44+
45+
/* for opt */
46+
static uint64_t period = 10000;
47+
static const char optString[] = "t:hc";
48+
static const char dev_name[] = "/dev/acrn_trace";
49+
50+
static uint32_t flags;
51+
static char trace_file_dir[TRACE_FILE_DIR_LEN];
52+
53+
static reader_struct *reader;
54+
static int pcpu_num = 0;
55+
56+
static void display_usage(void)
57+
{
58+
printf("acrntrace - tool to collect ACRN trace data\n"
59+
"[Usage] acrntrace [-t] [period in msec] [-ch]\n\n"
60+
"[Options]\n"
61+
"\t-h: print this message\n"
62+
"\t-t: period_in_ms: specify polling interval [1-999]\n"
63+
"\t-c: clear the buffered old data\n");
64+
}
65+
66+
static int parse_opt(int argc, char *argv[])
67+
{
68+
int opt, ret;
69+
70+
while ((opt = getopt(argc, argv, optString)) != -1) {
71+
switch (opt) {
72+
case 't':
73+
ret = atoi(optarg);
74+
if (ret <= 0 || ret >=1000) {
75+
pr_err("'-t' require integer between [1-999]\n");
76+
return -EINVAL;
77+
}
78+
period = ret * 1000;
79+
pr_dbg("Period is %lu\n", period);
80+
break;
81+
case 'c':
82+
flags |= FLAG_CLEAR_BUF;
83+
break;
84+
case 'h':
85+
display_usage();
86+
return -EINVAL;
87+
default:
88+
/* Undefined operation. */
89+
display_usage();
90+
return -EINVAL;
91+
}
92+
};
93+
return 0;
94+
}
95+
96+
static int shell_cmd(const char *cmd, char *outbuf, int len)
97+
{
98+
FILE *ptr;
99+
char cmd_buf[256];
100+
int ret;
101+
102+
if (!outbuf)
103+
return system(cmd);
104+
105+
memset(cmd_buf, 0, sizeof(cmd_buf));
106+
memset(outbuf, 0, len);
107+
snprintf(cmd_buf, sizeof(cmd_buf), "%s 2>&1", cmd);
108+
ptr = popen(cmd_buf, "re");
109+
if (!ptr)
110+
return -1;
111+
112+
ret = fread(outbuf, 1, len, ptr);
113+
pclose(ptr);
114+
115+
return ret;
116+
}
117+
118+
static int get_cpu_num(void)
119+
{
120+
121+
char cmd[128];
122+
char buf[16];
123+
int ret;
124+
125+
snprintf(cmd, sizeof(cmd), "ls %s_* | wc -l", dev_name);
126+
127+
ret = shell_cmd(cmd, buf, sizeof(buf));
128+
if (ret <= 0) {
129+
pr_err("Faile to get cpu number, use default 4\n");
130+
return PCPU_NUM;
131+
}
132+
133+
ret = atoi(buf);
134+
if (ret <= 0) {
135+
pr_err("Wrong cpu number, use default 4\n");
136+
return PCPU_NUM;
137+
}
138+
139+
return ret;
140+
}
141+
142+
static double get_cpu_freq(void)
143+
{
144+
char cmd[] =
145+
"cat /proc/cpuinfo | grep -m 1 \"cpu MHz\" | awk '{print $4}'";
146+
char buf[16];
147+
int ret;
148+
double freq = 0;
149+
150+
ret = shell_cmd(cmd, buf, sizeof(buf));
151+
152+
if (ret <= 0) {
153+
pr_err("Faile to get cpu freq, use default 1920MHz\n");
154+
return 1920.00;
155+
}
156+
157+
freq = atof(buf);
158+
if (freq <= 0) {
159+
pr_err("Invalid cpu freq string, use default 1920MHz\n");
160+
return 1920.00;
161+
}
162+
163+
return freq;
164+
}
165+
166+
static int create_trace_file_dir(char *dir)
167+
{
168+
int status;
169+
char cmd[CMD_MAX_LEN];
170+
char time_str[TIME_STR_LEN];
171+
time_t timep;
172+
struct tm *p;
173+
174+
time(&timep);
175+
p = localtime(&timep);
176+
snprintf(time_str, TIME_STR_LEN, "%d%02d%02d-%02d%02d%02d",
177+
(1900 + p->tm_year), (1 + p->tm_mon), p->tm_mday,
178+
p->tm_hour, p->tm_min, p->tm_sec);
179+
pr_info("start tracing at %s\n", time_str);
180+
181+
snprintf(dir, TRACE_FILE_DIR_LEN, "%s%s", TRACE_FILE_ROOT, time_str);
182+
183+
memset(cmd, 0, CMD_MAX_LEN);
184+
snprintf(cmd, CMD_MAX_LEN, "%s %s", "mkdir -p ", dir);
185+
186+
status = system(cmd);
187+
if (-1 == status)
188+
return -1; /* failed to execute sh */
189+
190+
pr_dbg("dir %s creted\n", dir);
191+
192+
return WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE;
193+
}
194+
195+
/* function executed in each consumer thread */
196+
static void reader_fn(param_t * param)
197+
{
198+
int ret;
199+
uint32_t cpuid = param->cpuid;
200+
FILE *fp = param->trace_filep;
201+
shared_buf_t *sbuf = param->sbuf;
202+
trace_ev_t e;
203+
204+
pr_dbg("reader thread[%lu] created for FILE*[0x%p]\n",
205+
pthread_self(), fp);
206+
207+
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL);
208+
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL);
209+
210+
/* Clear the old data in sbuf */
211+
if (flags & FLAG_CLEAR_BUF)
212+
sbuf_clear_buffered(sbuf);
213+
214+
/* write cpu freq to the first line of output file */
215+
fprintf(fp, "CPU Freq: %f\n", get_cpu_freq());
216+
217+
while (1) {
218+
do {
219+
ret = sbuf_get(sbuf, (void *)&e);
220+
if (ret == 0)
221+
break;
222+
else if (ret < 0) {
223+
pr_err("sbuf[%u] read error: %d\n", cpuid, ret);
224+
return;
225+
}
226+
227+
fprintf(fp, "%u | %lu | ", cpuid, e.tsc);
228+
switch (e.id) {
229+
/* defined in trace_event.h */
230+
/* for each ev type */
231+
ALL_CASES;
232+
}
233+
} while (ret > 0);
234+
235+
usleep(period);
236+
}
237+
}
238+
239+
static int create_reader(reader_struct * reader, uint32_t cpu)
240+
{
241+
char trace_file_name[TRACE_FILE_NAME_LEN];
242+
243+
snprintf(reader->dev_name, DEV_PATH_LEN, "%s_%u", dev_name, cpu);
244+
reader->param.cpuid = cpu;
245+
246+
reader->dev_fd = open(reader->dev_name, O_RDWR);
247+
if (reader->dev_fd < 0) {
248+
pr_err("Failed to open %s, err %d\n", reader->dev_name, errno);
249+
reader->dev_fd = 0;
250+
return -1;
251+
}
252+
253+
reader->param.sbuf = mmap(NULL, MMAP_SIZE,
254+
PROT_READ | PROT_WRITE,
255+
MAP_SHARED, reader->dev_fd, 0);
256+
if (reader->param.sbuf == MAP_FAILED) {
257+
pr_err("mmap failed for cpu%d, errno %d\n", cpu, errno);
258+
reader->param.sbuf = NULL;
259+
return -2;
260+
}
261+
262+
pr_dbg("sbuf[%d]:\nmagic_num: %lx\nele_num: %u\n ele_size: %u\n",
263+
cpu, reader->param.sbuf->magic, reader->param.sbuf->ele_num,
264+
reader->param.sbuf->ele_size);
265+
266+
snprintf(trace_file_name, TRACE_FILE_NAME_LEN, "%s/%d", trace_file_dir,
267+
cpu);
268+
reader->param.trace_filep = fopen(trace_file_name, "w+");
269+
if (!reader->param.trace_filep) {
270+
pr_err("Failed to open %s, err %d\n", trace_file_name, errno);
271+
return -3;
272+
}
273+
274+
pr_info("trace data file %s created for %s\n",
275+
trace_file_name, reader->dev_name);
276+
277+
if (pthread_create(&reader->thrd, NULL,
278+
(void *)&reader_fn, &reader->param)) {
279+
pr_err("failed to create reader thread, %d\n", cpu);
280+
return -4;
281+
}
282+
283+
return 0;
284+
}
285+
286+
static void destory_reader(reader_struct * reader)
287+
{
288+
if (reader->thrd) {
289+
pthread_cancel(reader->thrd);
290+
if (pthread_join(reader->thrd, NULL) != 0)
291+
pr_err("failed to cancel thread[%lu]\n", reader->thrd);
292+
else
293+
reader->thrd = 0;
294+
}
295+
296+
if (reader->param.sbuf) {
297+
munmap(reader->param.sbuf, MMAP_SIZE);
298+
reader->param.sbuf = NULL;
299+
}
300+
301+
if (reader->dev_fd) {
302+
close(reader->dev_fd);
303+
reader->dev_fd = 0;
304+
}
305+
306+
if (reader->param.trace_filep) {
307+
fflush(reader->param.trace_filep);
308+
fclose(reader->param.trace_filep);
309+
reader->param.trace_filep = NULL;
310+
}
311+
}
312+
313+
static void handle_on_exit(void)
314+
{
315+
uint32_t cpu;
316+
317+
/* if nothing to release */
318+
if (!(flags & FLAG_TO_REL))
319+
return;
320+
321+
pr_info("exiting - to release resources...\n");
322+
323+
foreach_cpu(cpu)
324+
destory_reader(&reader[cpu]);
325+
}
326+
327+
static void signal_exit_handler(int sig)
328+
{
329+
pr_info("exit on signal %d\n", sig);
330+
exit(0);
331+
}
332+
333+
int main(int argc, char *argv[])
334+
{
335+
uint32_t cpu = 0;
336+
337+
/* parse options */
338+
if (parse_opt(argc, argv))
339+
exit(EXIT_FAILURE);
340+
341+
/* how many cpus */
342+
pcpu_num = get_cpu_num();
343+
reader = malloc(sizeof(reader_struct) * pcpu_num);
344+
if (!reader) {
345+
pr_err("Failed to allocate reader memory\n");
346+
exit(EXIT_FAILURE);
347+
}
348+
memset(reader, 0, sizeof(reader_struct) * pcpu_num);
349+
350+
/* create dir for trace file */
351+
if (create_trace_file_dir(trace_file_dir)) {
352+
pr_err("Failed to create dir for trace files\n");
353+
exit(EXIT_FAILURE);
354+
}
355+
356+
atexit(handle_on_exit);
357+
358+
/* acquair res for each trace dev */
359+
flags |= FLAG_TO_REL;
360+
foreach_cpu(cpu)
361+
if (create_reader(&reader[cpu], cpu) < 0)
362+
goto out_free;
363+
364+
/* for kill exit handling */
365+
signal(SIGTERM, signal_exit_handler);
366+
signal(SIGINT, signal_exit_handler);
367+
368+
/* wait for user input to stop */
369+
printf("q <enter> to quit:\n");
370+
while (getchar() != 'q')
371+
printf("q <enter> to quit:\n");
372+
373+
out_free:
374+
foreach_cpu(cpu)
375+
destory_reader(&reader[cpu]);
376+
377+
free(reader);
378+
flags &= ~FLAG_TO_REL;
379+
380+
return EXIT_SUCCESS;
381+
}

0 commit comments

Comments
 (0)