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_OF
);
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_EXCTRC
);
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
], "evtcnt")) {
133 tmp
|= (1 << LIBSWO_PACKET_TYPE_DWT_EVTCNT
);
134 } else if (!g_ascii_strcasecmp(tokens
[i
], "exctrc")) {
135 tmp
|= (1 << LIBSWO_PACKET_TYPE_DWT_EXCTRC
);
136 } else if (!g_ascii_strcasecmp(tokens
[i
], "pcsample")) {
137 tmp
|= (1 << LIBSWO_PACKET_TYPE_DWT_PC_SAMPLE
);
138 } else if (!g_ascii_strcasecmp(tokens
[i
], "pcvalue")) {
139 tmp
|= (1 << LIBSWO_PACKET_TYPE_DWT_PC_VALUE
);
140 } else if (!g_ascii_strcasecmp(tokens
[i
], "addroffset")) {
141 tmp
|= (1 << LIBSWO_PACKET_TYPE_DWT_ADDR_OFFSET
);
142 } else if (!g_ascii_strcasecmp(tokens
[i
], "datavalue")) {
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_unknown_packet(const union libswo_packet
*packet
)
292 printf("Unknown data (size = %zu bytes)\n", packet
->unknown
.size
);
295 static void handle_sync_packet(const union libswo_packet
*packet
)
297 if (packet
->sync
.size
% 8)
298 printf("Synchronization (size = %zu bits)\n",
301 printf("Synchronization (size = %zu bytes)\n",
302 packet
->sync
.size
/ 8);
305 static void handle_overflow_packet(const union libswo_packet
*packet
)
308 printf("Overflow\n");
311 static void handle_lts_packet(const union libswo_packet
*packet
)
315 switch (packet
->lts
.relation
) {
316 case LIBSWO_LTS_REL_SYNC
:
319 case LIBSWO_LTS_REL_TS
:
320 tc
= "timestamp delayed";
322 case LIBSWO_LTS_REL_SRC
:
325 case LIBSWO_LTS_REL_BOTH
:
326 tc
= "data and timestamp delayed";
329 g_warning("Local timestamp packet with invalid relation: %u.",
330 packet
->lts
.relation
);
334 printf("Local timestamp (relation = %s, value = %x)\n", tc
,
338 static void handle_gts1_packet(const union libswo_packet
*packet
)
340 printf("Global timestamp (GTS1) (wrap = %u, clkch = %u, value = %x)\n",
341 packet
->gts1
.wrap
, packet
->gts1
.clkch
, packet
->gts1
.value
);
344 static void handle_gts2_packet(const union libswo_packet
*packet
)
346 printf("Global timestamp (GTS2) (value = %x)\n", packet
->gts2
.value
);
349 static void handle_ext_packet(const union libswo_packet
*packet
)
353 switch (packet
->ext
.source
) {
354 case LIBSWO_EXT_SRC_ITM
:
357 case LIBSWO_EXT_SRC_HW
:
361 g_warning("Extension packet with invalid source: %u.",
366 printf("Extension (source = %s, value = %x)\n", src
,
370 static void handle_inst_packet(const union libswo_packet
*packet
)
372 if (!(inst_address_filter
& (1 << packet
->inst
.address
)))
376 fwrite(packet
->inst
.payload
, packet
->inst
.size
- 1, 1, stdout
);
381 printf("Instrumentation (address = %u, value = %x, size = %zu bytes)\n",
382 packet
->inst
.address
, packet
->inst
.value
,
383 packet
->inst
.size
- 1);
386 static void handle_hw_packet(const union libswo_packet
*packet
)
388 printf("Hardware source (address = %u, value = %x, size = %zu bytes)\n",
389 packet
->hw
.address
, packet
->hw
.value
, packet
->hw
.size
- 1);
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_exctrc_packet(const union libswo_packet
*packet
)
407 switch (packet
->exctrc
.function
) {
408 case LIBSWO_EXCTRC_FUNC_ENTER
:
411 case LIBSWO_EXCTRC_FUNC_EXIT
:
414 case LIBSWO_EXCTRC_FUNC_RETURN
:
421 exception
= packet
->exctrc
.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_OF
:
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_EXCTRC
:
505 handle_dwt_exctrc_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: %i.", packet
->type
);
526 static void show_version(void)
528 printf("%s\n", SWODEC_VERSION_PACKAGE_STRING
);
529 printf("Using libswo %s\n", libswo_version_package_get_string());
532 static int parse_options(int *argc
, char ***argv
)
535 GOptionContext
*context
;
539 context
= g_option_context_new(NULL
);
540 g_option_context_add_main_entries(context
, entries
, NULL
);
542 if (!g_option_context_parse(context
, argc
, argv
, &error
)) {
543 g_critical("%s.", error
->message
);
545 g_option_context_free(context
);
549 g_option_context_free(context
);
554 static void log_handler(const gchar
*domain
, GLogLevelFlags level
,
555 const gchar
*message
, gpointer user_data
)
557 enum libswo_log_level tmp
;
562 if (level
& G_LOG_LEVEL_ERROR
)
563 tmp
= LIBSWO_LOG_LEVEL_ERROR
;
564 else if (level
& G_LOG_LEVEL_CRITICAL
)
565 tmp
= LIBSWO_LOG_LEVEL_ERROR
;
566 else if (level
& G_LOG_LEVEL_WARNING
)
567 tmp
= LIBSWO_LOG_LEVEL_WARNING
;
568 else if (level
& G_LOG_LEVEL_MESSAGE
)
569 tmp
= LIBSWO_LOG_LEVEL_INFO
;
570 else if (level
& G_LOG_LEVEL_INFO
)
571 tmp
= LIBSWO_LOG_LEVEL_INFO
;
572 else if (level
& G_LOG_LEVEL_DEBUG
)
573 tmp
= LIBSWO_LOG_LEVEL_DEBUG
;
575 tmp
= LIBSWO_LOG_LEVEL_WARNING
;
577 if (tmp
> opt_log_level
)
580 fprintf(stderr
, "%s\n", message
);
584 static int decoder_log_callback(struct libswo_context
*ctx
,
585 enum libswo_log_level level
, const char *format
, va_list args
,
591 if (level
> opt_log_level
)
594 fprintf(stderr
, "libswo: ");
595 vfprintf(stderr
, format
, args
);
596 fprintf(stderr
, "\n");
601 int main(int argc
, char **argv
)
604 struct libswo_context
*ctx
;
605 uint8_t buffer
[BUFFER_SIZE
];
612 opt_log_level
= LIBSWO_LOG_LEVEL_WARNING
;
613 opt_dump_inst
= FALSE
;
615 g_log_set_default_handler(&log_handler
, NULL
);
617 /* Disable packet filtering for all packet types by default. */
618 packet_type_filter
= (1 << LIBSWO_PACKET_TYPE_UNKNOWN
) | \
619 (1 << LIBSWO_PACKET_TYPE_SYNC
) | \
620 (1 << LIBSWO_PACKET_TYPE_OF
) | \
621 (1 << LIBSWO_PACKET_TYPE_LTS
) | \
622 (1 << LIBSWO_PACKET_TYPE_GTS1
) | \
623 (1 << LIBSWO_PACKET_TYPE_GTS2
) | \
624 (1 << LIBSWO_PACKET_TYPE_EXT
) | \
625 (1 << LIBSWO_PACKET_TYPE_INST
) | \
626 (1 << LIBSWO_PACKET_TYPE_HW
) | \
627 (1 << LIBSWO_PACKET_TYPE_DWT_EVTCNT
) | \
628 (1 << LIBSWO_PACKET_TYPE_DWT_EXCTRC
) | \
629 (1 << LIBSWO_PACKET_TYPE_DWT_PC_SAMPLE
) | \
630 (1 << LIBSWO_PACKET_TYPE_DWT_PC_VALUE
) | \
631 (1 << LIBSWO_PACKET_TYPE_DWT_ADDR_OFFSET
) | \
632 (1 << LIBSWO_PACKET_TYPE_DWT_DATA_VALUE
);
634 /* Disable instrumentation source address filtering by default. */
635 inst_address_filter
= 0xffffffff;
637 if (!parse_options(&argc
, &argv
))
646 packet_type_filter
= (1 << LIBSWO_PACKET_TYPE_INST
);
651 input
= g_io_channel_new_file(input_file
, "r", &error
);
654 g_critical("%s: %s.", input_file
, error
->message
);
662 input
= g_io_channel_unix_new(STDIN_FILENO
);
665 /* Set encoding to binary (default is UTF-8). */
666 iostat
= g_io_channel_set_encoding(input
, NULL
, &error
);
668 if (iostat
!= G_IO_STATUS_NORMAL
) {
669 g_critical("%s.", error
->message
);
671 g_io_channel_unref(input
);
675 g_io_channel_set_buffered(input
, FALSE
);
677 ret
= libswo_init(&ctx
, NULL
, BUFFER_SIZE
* 2);
679 if (ret
!= LIBSWO_OK
) {
680 g_critical("libswo_init() failed: %s.",
681 libswo_strerror_name(ret
));
682 g_io_channel_unref(input
);
686 ret
= libswo_set_callback(ctx
, &packet_cb
, NULL
);
688 if (ret
!= LIBSWO_OK
) {
689 g_critical("libswo_set_callback() failed: %s.",
690 libswo_strerror_name(ret
));
691 g_io_channel_unref(input
);
696 ret
= libswo_log_set_callback(ctx
, &decoder_log_callback
, NULL
);
698 if (ret
!= LIBSWO_OK
) {
699 g_critical("libswo_log_set_callback() failed: %s.",
700 libswo_strerror_name(ret
));
701 g_io_channel_unref(input
);
707 iostat
= g_io_channel_read_chars(input
, (gchar
*)buffer
,
708 BUFFER_SIZE
, &num
, &error
);
710 if (iostat
== G_IO_STATUS_ERROR
)
713 ret
= libswo_feed(ctx
, buffer
, num
);
715 if (ret
!= LIBSWO_OK
) {
716 g_critical("libswo_feed() failed: %s.",
717 libswo_strerror_name(ret
));
718 g_io_channel_unref(input
);
723 ret
= libswo_decode(ctx
, 0);
725 if (ret
!= LIBSWO_OK
) {
726 g_critical("libswo_decode() failed: %s.",
727 libswo_strerror_name(ret
));
728 g_io_channel_unref(input
);
733 if (iostat
== G_IO_STATUS_EOF
)
737 if (iostat
== G_IO_STATUS_ERROR
) {
738 g_critical("%s.", error
->message
);
740 g_io_channel_unref(input
);
745 ret
= libswo_decode(ctx
, LIBSWO_DF_EOS
);
747 if (ret
!= LIBSWO_OK
) {
748 g_critical("libswo_decode() failed: %s.",
749 libswo_strerror_name(ret
));
750 g_io_channel_unref(input
);
755 g_io_channel_unref(input
);