2 * This file is part of the swodec project.
4 * Copyright (C) 2014-2016 Marc Schink <swo-dev@marcschink.de>
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
31 #include <libswo/libswo.h>
36 * Exception names according to section B1.5 of ARMv7-M Architecture Reference
39 static const char *exception_names
[] = {
58 /* Number of exception names. */
59 #define NUM_EXCEPTION_NAMES 16
61 #define BUFFER_SIZE 1024
63 static gboolean opt_version
;
64 static enum libswo_log_level opt_log_level
;
65 static gchar
*input_file
= NULL
;
66 static uint32_t packet_type_filter
;
67 static uint32_t inst_address_filter
;
68 static gboolean opt_dump_inst
;
70 static gboolean
parse_filter_option(const gchar
*option_name
,
71 const gchar
*value
, gpointer data
, GError
**error
)
85 if (value
[0] == '~') {
93 tokens
= g_strsplit(value
, ",", -1);
97 g_strstrip(tokens
[i
]);
99 if (!strlen(tokens
[i
])) {
104 if (!g_ascii_strcasecmp(tokens
[i
], "unknown")) {
105 tmp
|= (1 << LIBSWO_PACKET_TYPE_UNKNOWN
);
106 } else if (!g_ascii_strcasecmp(tokens
[i
], "sync")) {
107 tmp
|= (1 << LIBSWO_PACKET_TYPE_SYNC
);
108 } else if (!g_ascii_strcasecmp(tokens
[i
], "of")) {
109 tmp
|= (1 << LIBSWO_PACKET_TYPE_OVERFLOW
);
110 } else if (!g_ascii_strcasecmp(tokens
[i
], "lts")) {
111 tmp
|= (1 << LIBSWO_PACKET_TYPE_LTS
);
112 } else if (!g_ascii_strcasecmp(tokens
[i
], "gts")) {
113 tmp
|= (1 << LIBSWO_PACKET_TYPE_GTS1
);
114 tmp
|= (1 << LIBSWO_PACKET_TYPE_GTS2
);
115 } else if (!g_ascii_strcasecmp(tokens
[i
], "gts1")) {
116 tmp
|= (1 << LIBSWO_PACKET_TYPE_GTS1
);
117 } else if (!g_ascii_strcasecmp(tokens
[i
], "gts2")) {
118 tmp
|= (1 << LIBSWO_PACKET_TYPE_GTS2
);
119 } else if (!g_ascii_strcasecmp(tokens
[i
], "ext")) {
120 tmp
|= (1 << LIBSWO_PACKET_TYPE_EXT
);
121 } else if (!g_ascii_strcasecmp(tokens
[i
], "inst")) {
122 tmp
|= (1 << LIBSWO_PACKET_TYPE_INST
);
123 } else if (!g_ascii_strcasecmp(tokens
[i
], "hw")) {
124 tmp
|= (1 << LIBSWO_PACKET_TYPE_HW
);
125 } else if (!g_ascii_strcasecmp(tokens
[i
], "dwt")) {
126 tmp
|= (1 << LIBSWO_PACKET_TYPE_DWT_EVTCNT
);
127 tmp
|= (1 << LIBSWO_PACKET_TYPE_DWT_EXCTRACE
);
128 tmp
|= (1 << LIBSWO_PACKET_TYPE_DWT_PC_SAMPLE
);
129 tmp
|= (1 << LIBSWO_PACKET_TYPE_DWT_PC_VALUE
);
130 tmp
|= (1 << LIBSWO_PACKET_TYPE_DWT_ADDR_OFFSET
);
131 tmp
|= (1 << LIBSWO_PACKET_TYPE_DWT_DATA_VALUE
);
132 } else if (!g_ascii_strcasecmp(tokens
[i
], "evcnt")) {
133 tmp
|= (1 << LIBSWO_PACKET_TYPE_DWT_EVTCNT
);
134 } else if (!g_ascii_strcasecmp(tokens
[i
], "exc")) {
135 tmp
|= (1 << LIBSWO_PACKET_TYPE_DWT_EXCTRACE
);
136 } else if (!g_ascii_strcasecmp(tokens
[i
], "pc")) {
137 tmp
|= (1 << LIBSWO_PACKET_TYPE_DWT_PC_SAMPLE
);
138 } else if (!g_ascii_strcasecmp(tokens
[i
], "dtpc")) {
139 tmp
|= (1 << LIBSWO_PACKET_TYPE_DWT_PC_VALUE
);
140 } else if (!g_ascii_strcasecmp(tokens
[i
], "dtaddr")) {
141 tmp
|= (1 << LIBSWO_PACKET_TYPE_DWT_ADDR_OFFSET
);
142 } else if (!g_ascii_strcasecmp(tokens
[i
], "dtval")) {
143 tmp
|= (1 << LIBSWO_PACKET_TYPE_DWT_DATA_VALUE
);
145 g_critical("Invalid packet type: %s.", tokens
[i
]);
157 * Apply the packet type filter only if at least one valid packet type
161 packet_type_filter
= tmp
;
168 static gboolean
parse_inst_filter_option(const gchar
*option_name
,
169 const gchar
*value
, gpointer data
, GError
**error
)
185 if (value
[0] == '~') {
193 tokens
= g_strsplit(value
, ",", -1);
197 g_strstrip(tokens
[i
]);
199 if (!strlen(tokens
[i
])) {
204 address
= strtoll(tokens
[i
], &endptr
, 10);
206 if (endptr
== tokens
[i
] || *endptr
!= '\0') {
207 g_critical("Invalid source address: %s.", tokens
[i
]);
212 if (address
< 0 || address
> LIBSWO_MAX_SOURCE_ADDRESS
) {
213 g_critical("Source address out of range: %li.",
219 tmp
|= (1 << address
);
227 * Apply the instrumentation source address filter only if at least one
228 * valid source address was specified.
231 inst_address_filter
= tmp
;
238 static gboolean
parse_log_level_option(const gchar
*option_name
,
239 const gchar
*value
, gpointer data
, GError
**error
)
248 if (!g_ascii_strcasecmp(value
, "none")) {
249 opt_log_level
= LIBSWO_LOG_LEVEL_NONE
;
250 } else if (!g_ascii_strcasecmp(value
, "error")) {
251 opt_log_level
= LIBSWO_LOG_LEVEL_ERROR
;
252 } else if (!g_ascii_strcasecmp(value
, "warn")) {
253 opt_log_level
= LIBSWO_LOG_LEVEL_WARNING
;
254 } else if (!g_ascii_strcasecmp(value
, "info")) {
255 opt_log_level
= LIBSWO_LOG_LEVEL_INFO
;
256 } else if (!g_ascii_strcasecmp(value
, "debug")) {
257 opt_log_level
= LIBSWO_LOG_LEVEL_DEBUG
;
260 tmp
= g_ascii_strtoull(value
, &endptr
, 10);
262 if (*endptr
!= '\0' || errno
!= 0 ||
263 tmp
> LIBSWO_LOG_LEVEL_DEBUG
) {
264 g_critical("Invalid log level: %s.", value
);
274 static GOptionEntry entries
[] = {
275 {"version", 'V', 0, G_OPTION_ARG_NONE
, &opt_version
,
276 "Show version information", NULL
},
277 {"log-level", 'l', 0, G_OPTION_ARG_CALLBACK
, &parse_log_level_option
,
279 {"input-file", 'i', 0, G_OPTION_ARG_FILENAME
, &input_file
,
280 "Load trace data from file", NULL
},
281 {"filter", 'f', 0, G_OPTION_ARG_CALLBACK
, &parse_filter_option
,
282 "Filter for packet types", NULL
},
283 {"filter-inst", 0, 0, G_OPTION_ARG_CALLBACK
, &parse_inst_filter_option
,
284 "Filter for instrumentation source addresses", NULL
},
285 {"dump-inst", 0, 0, G_OPTION_ARG_NONE
, &opt_dump_inst
,
286 "Dump instrumentation payload", NULL
},
287 {NULL
, 0, 0, 0, NULL
, NULL
, NULL
}
290 static void handle_hw_packet(const union libswo_packet
*packet
)
292 printf("Hardware source (address = %u, value = %x, size = %zu bytes)\n",
293 packet
->hw
.address
, packet
->hw
.value
, packet
->hw
.size
- 1);
296 static void handle_inst_packet(const union libswo_packet
*packet
)
298 if (!(inst_address_filter
& (1 << packet
->inst
.address
)))
302 fwrite(packet
->inst
.payload
, packet
->inst
.size
- 1, 1, stdout
);
307 printf("Instrumentation (address = %u, value = %x, size = %zu bytes)\n",
308 packet
->inst
.address
, packet
->inst
.value
,
309 packet
->inst
.size
- 1);
312 static void handle_overflow_packet(const union libswo_packet
*packet
)
315 printf("Overflow\n");
318 static void handle_ext_packet(const union libswo_packet
*packet
)
322 switch (packet
->ext
.source
) {
323 case LIBSWO_EXT_SRC_ITM
:
326 case LIBSWO_EXT_SRC_HW
:
330 g_warning("Extension packet with invalid source: %u.",
335 printf("Extension (source = %s, value = %x)\n", src
,
339 static void handle_unknown_packet(const union libswo_packet
*packet
)
341 printf("Unknown data (size = %zu bytes)\n", packet
->unknown
.size
);
344 static void handle_sync_packet(const union libswo_packet
*packet
)
346 if (packet
->sync
.size
% 8)
347 printf("Synchronization (size = %zu bits)\n",
350 printf("Synchronization (size = %zu bytes)\n",
351 packet
->sync
.size
/ 8);
354 static void handle_lts_packet(const union libswo_packet
*packet
)
358 switch (packet
->lts
.relation
) {
359 case LIBSWO_LTS_REL_SYNC
:
362 case LIBSWO_LTS_REL_TS
:
363 tc
= "timestamp delayed";
365 case LIBSWO_LTS_REL_SRC
:
368 case LIBSWO_LTS_REL_BOTH
:
369 tc
= "data and timestamp delayed";
372 g_warning("Local timestamp packet with invalid relation: %u.",
373 packet
->lts
.relation
);
377 printf("Local timestamp (relation = %s, value = %x)\n", tc
,
381 static void handle_gts1_packet(const union libswo_packet
*packet
)
383 printf("Global timestamp (GTS1) (wrap = %u, clkch = %u, value = %x)\n",
384 packet
->gts1
.wrap
, packet
->gts1
.clkch
, packet
->gts1
.value
);
387 static void handle_gts2_packet(const union libswo_packet
*packet
)
389 printf("Global timestamp (GTS2) (value = %x)\n", packet
->gts2
.value
);
392 static void handle_dwt_evtcnt_packet(const union libswo_packet
*packet
)
394 printf("Event counter (CPI = %u, exc = %u, sleep = %u, LSU = %u, "
395 "fold = %u, cyc = %u)\n", packet
->evtcnt
.cpi
,
396 packet
->evtcnt
.exc
, packet
->evtcnt
.sleep
, packet
->evtcnt
.lsu
,
397 packet
->evtcnt
.fold
, packet
->evtcnt
.cyc
);
400 static void handle_dwt_exctrace_packet(const union libswo_packet
*packet
)
407 switch (packet
->exctrace
.function
) {
408 case LIBSWO_EXCTRACE_FUNC_ENTER
:
411 case LIBSWO_EXCTRACE_FUNC_EXIT
:
414 case LIBSWO_EXCTRACE_FUNC_RETURN
:
421 exception
= packet
->exctrace
.exception
;
423 if (exception
< NUM_EXCEPTION_NAMES
) {
424 name
= exception_names
[exception
];
426 snprintf(buf
, sizeof(buf
), "External interrupt %u",
427 exception
- NUM_EXCEPTION_NAMES
);
431 printf("Exception trace (function = %s, exception = %s)\n", func
,
435 static void handle_dwt_pc_sample_packet(const union libswo_packet
*packet
)
437 if (packet
->pc_sample
.sleep
)
438 printf("Periodic PC sleep\n");
440 printf("Periodic PC sample (value = %x)\n",
441 packet
->pc_sample
.pc
);
444 static void handle_dwt_pc_value_packet(const union libswo_packet
*packet
)
446 printf("Data trace PC value (comparator = %u, value = %x)\n",
447 packet
->pc_value
.cmpn
, packet
->pc_value
.pc
);
450 static void handle_dwt_addr_offset_packet(const union libswo_packet
*packet
)
452 printf("Data trace address offset (comparator = %u, value = %x)\n",
453 packet
->addr_offset
.cmpn
, packet
->addr_offset
.offset
);
456 static void handle_dwt_data_value_packet(const union libswo_packet
*packet
)
458 printf("Data trace data value (comparator = %u, WnR = %u, value = %x, "
459 "size = %zu bytes)\n", packet
->data_value
.cmpn
,
460 packet
->data_value
.wnr
, packet
->data_value
.data_value
,
461 packet
->data_value
.size
- 1);
464 static int packet_cb(struct libswo_context
*ctx
,
465 const union libswo_packet
*packet
, void *user_data
)
470 if (!(packet_type_filter
& (1 << packet
->type
)))
473 switch (packet
->type
) {
474 case LIBSWO_PACKET_TYPE_UNKNOWN
:
475 handle_unknown_packet(packet
);
477 case LIBSWO_PACKET_TYPE_SYNC
:
478 handle_sync_packet(packet
);
480 case LIBSWO_PACKET_TYPE_INST
:
481 handle_inst_packet(packet
);
483 case LIBSWO_PACKET_TYPE_OVERFLOW
:
484 handle_overflow_packet(packet
);
486 case LIBSWO_PACKET_TYPE_EXT
:
487 handle_ext_packet(packet
);
489 case LIBSWO_PACKET_TYPE_LTS
:
490 handle_lts_packet(packet
);
492 case LIBSWO_PACKET_TYPE_GTS1
:
493 handle_gts1_packet(packet
);
495 case LIBSWO_PACKET_TYPE_GTS2
:
496 handle_gts2_packet(packet
);
498 case LIBSWO_PACKET_TYPE_HW
:
499 handle_hw_packet(packet
);
501 case LIBSWO_PACKET_TYPE_DWT_EVTCNT
:
502 handle_dwt_evtcnt_packet(packet
);
504 case LIBSWO_PACKET_TYPE_DWT_EXCTRACE
:
505 handle_dwt_exctrace_packet(packet
);
507 case LIBSWO_PACKET_TYPE_DWT_PC_SAMPLE
:
508 handle_dwt_pc_sample_packet(packet
);
510 case LIBSWO_PACKET_TYPE_DWT_PC_VALUE
:
511 handle_dwt_pc_value_packet(packet
);
513 case LIBSWO_PACKET_TYPE_DWT_ADDR_OFFSET
:
514 handle_dwt_addr_offset_packet(packet
);
516 case LIBSWO_PACKET_TYPE_DWT_DATA_VALUE
:
517 handle_dwt_data_value_packet(packet
);
520 g_warning("Invalid packet type: %u.", packet
->type
);
527 static void show_version(void)
529 printf("%s\n", SWODEC_VERSION_PACKAGE_STRING
);
530 printf("Using libswo %s\n", libswo_version_package_get_string());
533 static int parse_options(int *argc
, char ***argv
)
536 GOptionContext
*context
;
540 context
= g_option_context_new(NULL
);
541 g_option_context_add_main_entries(context
, entries
, NULL
);
543 if (!g_option_context_parse(context
, argc
, argv
, &error
)) {
544 g_critical("%s.", error
->message
);
546 g_option_context_free(context
);
550 g_option_context_free(context
);
555 static void log_handler(const gchar
*domain
, GLogLevelFlags level
,
556 const gchar
*message
, gpointer user_data
)
558 enum libswo_log_level tmp
;
563 if (level
& G_LOG_LEVEL_ERROR
)
564 tmp
= LIBSWO_LOG_LEVEL_ERROR
;
565 else if (level
& G_LOG_LEVEL_CRITICAL
)
566 tmp
= LIBSWO_LOG_LEVEL_ERROR
;
567 else if (level
& G_LOG_LEVEL_WARNING
)
568 tmp
= LIBSWO_LOG_LEVEL_WARNING
;
569 else if (level
& G_LOG_LEVEL_MESSAGE
)
570 tmp
= LIBSWO_LOG_LEVEL_INFO
;
571 else if (level
& G_LOG_LEVEL_INFO
)
572 tmp
= LIBSWO_LOG_LEVEL_INFO
;
573 else if (level
& G_LOG_LEVEL_DEBUG
)
574 tmp
= LIBSWO_LOG_LEVEL_DEBUG
;
576 tmp
= LIBSWO_LOG_LEVEL_WARNING
;
578 if (tmp
> opt_log_level
)
581 fprintf(stderr
, "%s\n", message
);
585 static int decoder_log_callback(struct libswo_context
*ctx
,
586 enum libswo_log_level level
, const char *format
, va_list args
,
592 if (level
> opt_log_level
)
595 fprintf(stderr
, "libswo: ");
596 vfprintf(stderr
, format
, args
);
597 fprintf(stderr
, "\n");
602 int main(int argc
, char **argv
)
605 struct libswo_context
*ctx
;
606 uint8_t buffer
[BUFFER_SIZE
];
613 opt_log_level
= LIBSWO_LOG_LEVEL_WARNING
;
614 opt_dump_inst
= FALSE
;
616 g_log_set_default_handler(&log_handler
, NULL
);
618 /* Disable packet filtering for all packet types by default. */
619 packet_type_filter
= (1 << LIBSWO_PACKET_TYPE_UNKNOWN
) | \
620 (1 << LIBSWO_PACKET_TYPE_SYNC
) | \
621 (1 << LIBSWO_PACKET_TYPE_OVERFLOW
) | \
622 (1 << LIBSWO_PACKET_TYPE_LTS
) | \
623 (1 << LIBSWO_PACKET_TYPE_GTS1
) | \
624 (1 << LIBSWO_PACKET_TYPE_GTS2
) | \
625 (1 << LIBSWO_PACKET_TYPE_EXT
) | \
626 (1 << LIBSWO_PACKET_TYPE_INST
) | \
627 (1 << LIBSWO_PACKET_TYPE_HW
) | \
628 (1 << LIBSWO_PACKET_TYPE_DWT_EVTCNT
) | \
629 (1 << LIBSWO_PACKET_TYPE_DWT_EXCTRACE
) | \
630 (1 << LIBSWO_PACKET_TYPE_DWT_PC_SAMPLE
) | \
631 (1 << LIBSWO_PACKET_TYPE_DWT_PC_VALUE
) | \
632 (1 << LIBSWO_PACKET_TYPE_DWT_ADDR_OFFSET
) | \
633 (1 << LIBSWO_PACKET_TYPE_DWT_DATA_VALUE
);
635 /* Disable instrumentation source address filtering by default. */
636 inst_address_filter
= 0xffffffff;
638 if (!parse_options(&argc
, &argv
))
647 packet_type_filter
= (1 << LIBSWO_PACKET_TYPE_INST
);
652 input
= g_io_channel_new_file(input_file
, "r", &error
);
655 g_critical("%s: %s.", input_file
, error
->message
);
663 input
= g_io_channel_unix_new(STDIN_FILENO
);
666 /* Set encoding to binary (default is UTF-8). */
667 iostat
= g_io_channel_set_encoding(input
, NULL
, &error
);
669 if (iostat
!= G_IO_STATUS_NORMAL
) {
670 g_critical("%s.", error
->message
);
672 g_io_channel_unref(input
);
676 g_io_channel_set_buffered(input
, FALSE
);
678 ret
= libswo_init(&ctx
, NULL
, BUFFER_SIZE
* 2);
680 if (ret
!= LIBSWO_OK
) {
681 g_critical("libswo_init() failed: %s.",
682 libswo_strerror_name(ret
));
683 g_io_channel_unref(input
);
687 ret
= libswo_set_callback(ctx
, &packet_cb
, NULL
);
689 if (ret
!= LIBSWO_OK
) {
690 g_critical("libswo_set_callback() failed: %s.",
691 libswo_strerror_name(ret
));
692 g_io_channel_unref(input
);
697 ret
= libswo_log_set_callback(ctx
, &decoder_log_callback
, NULL
);
699 if (ret
!= LIBSWO_OK
) {
700 g_critical("libswo_log_set_callback() failed: %s.",
701 libswo_strerror_name(ret
));
702 g_io_channel_unref(input
);
708 iostat
= g_io_channel_read_chars(input
, (gchar
*)buffer
,
709 BUFFER_SIZE
, &num
, &error
);
711 if (iostat
== G_IO_STATUS_ERROR
)
714 ret
= libswo_feed(ctx
, buffer
, num
);
716 if (ret
!= LIBSWO_OK
) {
717 g_critical("libswo_feed() failed: %s.",
718 libswo_strerror_name(ret
));
719 g_io_channel_unref(input
);
724 ret
= libswo_decode(ctx
, 0);
726 if (ret
!= LIBSWO_OK
) {
727 g_critical("libswo_decode() failed: %s.",
728 libswo_strerror_name(ret
));
729 g_io_channel_unref(input
);
734 if (iostat
== G_IO_STATUS_EOF
)
738 if (iostat
== G_IO_STATUS_ERROR
) {
739 g_critical("%s.", error
->message
);
741 g_io_channel_unref(input
);
746 ret
= libswo_decode(ctx
, LIBSWO_DF_EOS
);
748 if (ret
!= LIBSWO_OK
) {
749 g_critical("libswo_decode() failed: %s.",
750 libswo_strerror_name(ret
));
751 g_io_channel_unref(input
);
756 g_io_channel_unref(input
);