4 * Copyright (c) Intel Corporation.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
17 * * Neither the name of Intel Corporation nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include "spdk/stdinc.h"
37 #include "spdk/string.h"
38 #include "spdk/trace.h"
39 #include "spdk/util.h"
40 #include "spdk/barrier.h"
42 #define TRACE_FILE_COPY_SIZE (32 * 1024)
43 #define TRACE_PATH_MAX 2048
45 static char *g_exe_name
;
46 static int g_verbose
= 1;
47 static uint64_t g_tsc_rate
;
48 static uint64_t g_utsc_rate
;
49 static bool g_shutdown
= false;
50 static uint64_t g_histories_size
;
52 struct lcore_trace_record_ctx
{
53 char lcore_file
[TRACE_PATH_MAX
];
55 struct spdk_trace_history
*in_history
;
56 struct spdk_trace_history
*out_history
;
58 /* Recorded next entry index in record */
59 uint64_t rec_next_entry
;
61 /* Record tsc for report */
62 uint64_t first_entry_tsc
;
63 uint64_t last_entry_tsc
;
65 /* Total number of entries in lcore trace file */
69 struct aggr_trace_record_ctx
{
73 struct lcore_trace_record_ctx lcore_ports
[SPDK_TRACE_MAX_LCORE
];
74 struct spdk_trace_histories
*trace_histories
;
78 input_trace_file_mmap(struct aggr_trace_record_ctx
*ctx
, const char *shm_name
)
83 ctx
->shm_fd
= shm_open(shm_name
, O_RDONLY
, 0);
84 if (ctx
->shm_fd
< 0) {
85 fprintf(stderr
, "Could not open %s.\n", shm_name
);
89 /* Map the header of trace file */
90 history_ptr
= mmap(NULL
, sizeof(struct spdk_trace_histories
), PROT_READ
, MAP_SHARED
, ctx
->shm_fd
,
92 if (history_ptr
== MAP_FAILED
) {
93 fprintf(stderr
, "Could not mmap shm %s.\n", shm_name
);
98 ctx
->trace_histories
= (struct spdk_trace_histories
*)history_ptr
;
100 g_tsc_rate
= ctx
->trace_histories
->flags
.tsc_rate
;
101 g_utsc_rate
= g_tsc_rate
/ 1000;
102 if (g_tsc_rate
== 0) {
103 fprintf(stderr
, "Invalid tsc_rate %ju\n", g_tsc_rate
);
104 munmap(history_ptr
, sizeof(struct spdk_trace_histories
));
110 printf("TSC Rate: %ju\n", g_tsc_rate
);
113 /* Remap the entire trace file */
114 g_histories_size
= spdk_get_trace_histories_size(ctx
->trace_histories
);
115 munmap(history_ptr
, sizeof(struct spdk_trace_histories
));
116 history_ptr
= mmap(NULL
, g_histories_size
, PROT_READ
, MAP_SHARED
, ctx
->shm_fd
, 0);
117 if (history_ptr
== MAP_FAILED
) {
118 fprintf(stderr
, "Could not remmap shm %s.\n", shm_name
);
123 ctx
->trace_histories
= (struct spdk_trace_histories
*)history_ptr
;
124 for (i
= 0; i
< SPDK_TRACE_MAX_LCORE
; i
++) {
125 ctx
->lcore_ports
[i
].in_history
= spdk_get_per_lcore_history(ctx
->trace_histories
, i
);
128 printf("Number of trace entries for lcore (%d): %ju\n", i
,
129 ctx
->lcore_ports
[i
].in_history
->num_entries
);
137 output_trace_files_prepare(struct aggr_trace_record_ctx
*ctx
, const char *aggr_path
)
139 int flags
= O_CREAT
| O_EXCL
| O_RDWR
;
140 struct lcore_trace_record_ctx
*port_ctx
;
144 /* Assign file names for related trace files */
145 ctx
->out_file
= aggr_path
;
146 for (i
= 0; i
< SPDK_TRACE_MAX_LCORE
; i
++) {
147 port_ctx
= &ctx
->lcore_ports
[i
];
149 /* Get the length of trace file name for each lcore with format "%s-%d" */
150 name_len
= snprintf(port_ctx
->lcore_file
, TRACE_PATH_MAX
, "%s-%d", ctx
->out_file
, i
);
151 if (name_len
>= TRACE_PATH_MAX
) {
152 fprintf(stderr
, "Length of file path (%s) exceeds limitation for lcore file.\n",
158 /* If output trace file already exists, try to unlink it together with its temporary files */
159 if (access(ctx
->out_file
, F_OK
) == 0) {
160 rc
= unlink(ctx
->out_file
);
165 for (i
= 0; i
< SPDK_TRACE_MAX_LCORE
; i
++) {
166 port_ctx
= &ctx
->lcore_ports
[i
];
167 if (access(port_ctx
->lcore_file
, F_OK
) == 0) {
168 rc
= unlink(port_ctx
->lcore_file
);
177 for (i
= 0; i
< SPDK_TRACE_MAX_LCORE
; i
++) {
178 port_ctx
= &ctx
->lcore_ports
[i
];
180 port_ctx
->fd
= open(port_ctx
->lcore_file
, flags
, 0600);
181 if (port_ctx
->fd
< 0) {
182 fprintf(stderr
, "Could not open lcore file %s.\n", port_ctx
->lcore_file
);
187 printf("Create tmp lcore trace file %s for lcore %d\n", port_ctx
->lcore_file
, i
);
190 port_ctx
->out_history
= calloc(1, sizeof(struct spdk_trace_history
));
191 if (port_ctx
->out_history
== NULL
) {
192 fprintf(stderr
, "Failed to allocate memory for out_history.\n");
200 for (i
= 0; i
< SPDK_TRACE_MAX_LCORE
; i
++) {
201 port_ctx
= &ctx
->lcore_ports
[i
];
202 free(port_ctx
->out_history
);
204 if (port_ctx
->fd
> 0) {
213 output_trace_files_finish(struct aggr_trace_record_ctx
*ctx
)
215 struct lcore_trace_record_ctx
*port_ctx
;
218 for (i
= 0; i
< SPDK_TRACE_MAX_LCORE
; i
++) {
219 port_ctx
= &ctx
->lcore_ports
[i
];
221 free(port_ctx
->out_history
);
223 unlink(port_ctx
->lcore_file
);
226 printf("Remove tmp lcore trace file %s for lcore %d\n", port_ctx
->lcore_file
, i
);
232 cont_write(int fildes
, const void *buf
, size_t nbyte
)
238 rc
= write(fildes
, buf
, _nbyte
);
240 if (errno
!= EINTR
) {
254 cont_read(int fildes
, void *buf
, size_t nbyte
)
260 rc
= read(fildes
, buf
, _nbyte
);
262 return nbyte
- _nbyte
;
264 if (errno
!= EINTR
) {
278 lcore_trace_last_entry_idx(struct spdk_trace_history
*in_history
, int cir_next_idx
)
282 if (cir_next_idx
== 0) {
283 last_idx
= in_history
->num_entries
- 1;
285 last_idx
= cir_next_idx
- 1;
292 circular_buffer_padding_backward(int fd
, struct spdk_trace_history
*in_history
,
293 int cir_start
, int cir_end
)
297 if (cir_end
<= cir_start
) {
298 fprintf(stderr
, "Wrong using of circular_buffer_padding_back\n");
302 rc
= cont_write(fd
, &in_history
->entries
[cir_start
],
303 sizeof(struct spdk_trace_entry
) * (cir_end
- cir_start
));
305 fprintf(stderr
, "Failed to append entries into lcore file\n");
313 circular_buffer_padding_across(int fd
, struct spdk_trace_history
*in_history
,
314 int cir_start
, int cir_end
)
317 int num_entries
= in_history
->num_entries
;
319 if (cir_end
> cir_start
) {
320 fprintf(stderr
, "Wrong using of circular_buffer_padding_across\n");
324 rc
= cont_write(fd
, &in_history
->entries
[cir_start
],
325 sizeof(struct spdk_trace_entry
) * (num_entries
- cir_start
));
327 fprintf(stderr
, "Failed to append entries into lcore file backward\n");
335 rc
= cont_write(fd
, &in_history
->entries
[0], sizeof(struct spdk_trace_entry
) * cir_end
);
337 fprintf(stderr
, "Failed to append entries into lcore file forward\n");
345 circular_buffer_padding_all(int fd
, struct spdk_trace_history
*in_history
,
348 return circular_buffer_padding_across(fd
, in_history
, cir_end
, cir_end
);
352 lcore_trace_record(struct lcore_trace_record_ctx
*lcore_port
)
354 struct spdk_trace_history
*in_history
= lcore_port
->in_history
;
355 uint64_t rec_next_entry
= lcore_port
->rec_next_entry
;
356 uint64_t rec_num_entries
= lcore_port
->num_entries
;
357 int fd
= lcore_port
->fd
;
358 uint64_t shm_next_entry
;
359 uint64_t num_cir_entries
;
360 uint64_t shm_cir_next
;
361 uint64_t rec_cir_next
;
365 shm_next_entry
= in_history
->next_entry
;
367 /* Ensure all entries of spdk_trace_history are latest to next_entry */
370 if (shm_next_entry
== rec_next_entry
) {
371 /* There is no update */
373 } else if (shm_next_entry
< rec_next_entry
) {
375 fprintf(stderr
, "Trace porting error in lcore %d, trace rollback occurs.\n", in_history
->lcore
);
376 fprintf(stderr
, "shm_next_entry is %ju, record_next_entry is %ju.\n", shm_next_entry
,
381 num_cir_entries
= in_history
->num_entries
;
382 shm_cir_next
= shm_next_entry
& (num_cir_entries
- 1);
384 /* Record first entry's tsc and corresponding entries when recording first time. */
385 if (lcore_port
->first_entry_tsc
== 0) {
386 if (shm_next_entry
< num_cir_entries
) {
387 /* Updates haven't been across circular buffer yet.
388 * The first entry in shared memory is the eldest one.
390 lcore_port
->first_entry_tsc
= in_history
->entries
[0].tsc
;
392 lcore_port
->num_entries
+= shm_cir_next
;
393 rc
= circular_buffer_padding_backward(fd
, in_history
, 0, shm_cir_next
);
395 /* Updates have already been across circular buffer.
396 * The eldest entry in shared memory is pointed by shm_cir_next.
398 lcore_port
->first_entry_tsc
= in_history
->entries
[shm_cir_next
].tsc
;
400 lcore_port
->num_entries
+= num_cir_entries
;
401 rc
= circular_buffer_padding_all(fd
, in_history
, shm_cir_next
);
407 if (shm_next_entry
- rec_next_entry
> num_cir_entries
) {
408 /* There must be missed updates */
409 fprintf(stderr
, "Trace-record missed %ju trace entries\n",
410 shm_next_entry
- rec_next_entry
- num_cir_entries
);
412 lcore_port
->num_entries
+= num_cir_entries
;
413 rc
= circular_buffer_padding_all(fd
, in_history
, shm_cir_next
);
414 } else if (shm_next_entry
- rec_next_entry
== num_cir_entries
) {
415 /* All circular buffer is updated */
416 lcore_port
->num_entries
+= num_cir_entries
;
417 rc
= circular_buffer_padding_all(fd
, in_history
, shm_cir_next
);
419 /* Part of circular buffer is updated */
420 rec_cir_next
= rec_next_entry
& (num_cir_entries
- 1);
422 if (shm_cir_next
> rec_cir_next
) {
423 /* Updates are not across circular buffer */
424 lcore_port
->num_entries
+= shm_cir_next
- rec_cir_next
;
425 rc
= circular_buffer_padding_backward(fd
, in_history
, rec_cir_next
, shm_cir_next
);
427 /* Updates are across circular buffer */
428 lcore_port
->num_entries
+= num_cir_entries
- rec_cir_next
+ shm_cir_next
;
429 rc
= circular_buffer_padding_across(fd
, in_history
, rec_cir_next
, shm_cir_next
);
439 printf("Append %ju trace_entry for lcore %d\n", lcore_port
->num_entries
- rec_num_entries
,
443 /* Update tpoint_count info */
444 memcpy(lcore_port
->out_history
, lcore_port
->in_history
, sizeof(struct spdk_trace_history
));
446 /* Update last_entry_tsc to align with appended entries */
447 last_idx
= lcore_trace_last_entry_idx(in_history
, shm_cir_next
);
448 lcore_port
->last_entry_tsc
= in_history
->entries
[last_idx
].tsc
;
449 lcore_port
->rec_next_entry
= shm_next_entry
;
455 trace_files_aggregate(struct aggr_trace_record_ctx
*ctx
)
457 int flags
= O_CREAT
| O_EXCL
| O_RDWR
;
458 struct lcore_trace_record_ctx
*lcore_port
;
459 char copy_buff
[TRACE_FILE_COPY_SIZE
];
460 uint64_t lcore_offsets
[SPDK_TRACE_MAX_LCORE
+ 1];
465 ctx
->out_fd
= open(ctx
->out_file
, flags
, 0600);
466 if (ctx
->out_fd
< 0) {
467 fprintf(stderr
, "Could not open aggregation file %s.\n", ctx
->out_file
);
472 printf("Create trace file %s for output\n", ctx
->out_file
);
475 /* Write flags of histories into head of converged trace file, except num_entriess */
476 rc
= cont_write(ctx
->out_fd
, ctx
->trace_histories
,
477 sizeof(struct spdk_trace_histories
) - sizeof(lcore_offsets
));
479 fprintf(stderr
, "Failed to write trace header into trace file\n");
483 /* Update and append lcore offsets converged trace file */
484 lcore_offsets
[0] = sizeof(struct spdk_trace_flags
);
485 for (i
= 1; i
< (int)SPDK_COUNTOF(lcore_offsets
); i
++) {
486 lcore_offsets
[i
] = spdk_get_trace_history_size(ctx
->lcore_ports
[i
- 1].num_entries
) +
487 lcore_offsets
[i
- 1];
490 rc
= cont_write(ctx
->out_fd
, lcore_offsets
, sizeof(lcore_offsets
));
492 fprintf(stderr
, "Failed to write lcore offsets into trace file\n");
496 /* Append each lcore trace file into converged trace file */
497 for (i
= 0; i
< SPDK_TRACE_MAX_LCORE
; i
++) {
498 lcore_port
= &ctx
->lcore_ports
[i
];
500 lcore_port
->out_history
->num_entries
= lcore_port
->num_entries
;
501 rc
= cont_write(ctx
->out_fd
, lcore_port
->out_history
, sizeof(struct spdk_trace_history
));
503 fprintf(stderr
, "Failed to write lcore trace header into trace file\n");
507 /* Move file offset to the start of trace_entries */
508 rc
= lseek(lcore_port
->fd
, 0, SEEK_SET
);
510 fprintf(stderr
, "Failed to lseek lcore trace file\n");
515 while ((len
= cont_read(lcore_port
->fd
, copy_buff
, TRACE_FILE_COPY_SIZE
)) > 0) {
517 rc
= cont_write(ctx
->out_fd
, copy_buff
, len
);
519 fprintf(stderr
, "Failed to write lcore trace entries into trace file\n");
524 if (len_sum
!= lcore_port
->num_entries
* sizeof(struct spdk_trace_entry
)) {
525 fprintf(stderr
, "Len of lcore trace file doesn't match number of entries for lcore\n");
529 printf("All lcores trace entries are aggregated into trace file %s\n", ctx
->out_file
);
538 __shutdown_signal(int signo
)
544 setup_exit_signal_handler(void)
546 struct sigaction sigact
;
549 memset(&sigact
, 0, sizeof(sigact
));
550 sigemptyset(&sigact
.sa_mask
);
551 /* Install the same handler for SIGINT and SIGTERM */
552 sigact
.sa_handler
= __shutdown_signal
;
554 rc
= sigaction(SIGINT
, &sigact
, NULL
);
556 fprintf(stderr
, "sigaction(SIGINT) failed\n");
561 rc
= sigaction(SIGTERM
, &sigact
, NULL
);
563 fprintf(stderr
, "sigaction(SIGTERM) failed\n");
569 static void usage(void)
571 printf("\n%s is used to record all SPDK generated trace entries\n", g_exe_name
);
572 printf("from SPDK trace shared-memory to specified file.\n\n");
574 printf(" %s <option>\n", g_exe_name
);
575 printf(" option = '-q' to disable verbose mode\n");
576 printf(" '-s' to specify spdk_trace shm name for a\n");
577 printf(" currently running process\n");
578 printf(" '-i' to specify the shared memory ID\n");
579 printf(" '-p' to specify the trace PID\n");
580 printf(" (one of -i or -p must be specified)\n");
581 printf(" '-f' to specify output trace file name\n");
582 printf(" '-h' to print usage information\n");
585 int main(int argc
, char **argv
)
587 const char *app_name
= NULL
;
588 const char *file_name
= NULL
;
591 int shm_id
= -1, shm_pid
= -1;
594 struct aggr_trace_record_ctx ctx
= {};
595 struct lcore_trace_record_ctx
*lcore_port
;
597 g_exe_name
= argv
[0];
598 while ((op
= getopt(argc
, argv
, "f:i:p:qs:h")) != -1) {
601 shm_id
= spdk_strtol(optarg
, 10);
604 shm_pid
= spdk_strtol(optarg
, 10);
622 if (file_name
== NULL
) {
623 fprintf(stderr
, "-f must be specified\n");
628 if (app_name
== NULL
) {
629 fprintf(stderr
, "-s must be specified\n");
634 if (shm_id
== -1 && shm_pid
== -1) {
635 fprintf(stderr
, "-i or -p must be specified\n");
641 snprintf(shm_name
, sizeof(shm_name
), "/%s_trace.%d", app_name
, shm_id
);
643 snprintf(shm_name
, sizeof(shm_name
), "/%s_trace.pid%d", app_name
, shm_pid
);
646 rc
= setup_exit_signal_handler();
651 rc
= input_trace_file_mmap(&ctx
, shm_name
);
656 rc
= output_trace_files_prepare(&ctx
, file_name
);
661 printf("Start to poll trace shm file %s\n", shm_name
);
662 while (!g_shutdown
&& rc
== 0) {
663 for (i
= 0; i
< SPDK_TRACE_MAX_LCORE
; i
++) {
664 lcore_port
= &ctx
.lcore_ports
[i
];
666 rc
= lcore_trace_record(lcore_port
);
677 printf("Start to aggregate lcore trace files\n");
678 rc
= trace_files_aggregate(&ctx
);
684 printf("TSC Rate: %ju\n", g_tsc_rate
);
685 for (i
= 0; i
< SPDK_TRACE_MAX_LCORE
; i
++) {
686 lcore_port
= &ctx
.lcore_ports
[i
];
688 if (lcore_port
->num_entries
== 0) {
692 printf("Port %ju trace entries for lcore (%d) in %ju usec\n",
693 lcore_port
->num_entries
, i
,
694 (lcore_port
->last_entry_tsc
- lcore_port
->first_entry_tsc
) / g_utsc_rate
);
698 munmap(ctx
.trace_histories
, g_histories_size
);
701 output_trace_files_finish(&ctx
);