Add filter support for DWT packets
[swodec.git] / src / main.c
blobeb33472a5898eec902d8d54c1b8a7122a5db743c
1 /*
2 * This file is part of the swodec project.
4 * Copyright (C) 2014-2015 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/>.
20 #include <stdio.h>
21 #include <stdint.h>
22 #include <unistd.h>
23 #include <string.h>
24 #include <glib.h>
26 #include "config.h"
27 #include "swodec.h"
29 #define BUFFER_SIZE 1024
31 static gboolean opt_version;
32 static gchar *input_file = NULL;
33 uint16_t packet_type_filter;
34 static uint32_t inst_address_filter;
35 static gboolean opt_dump_inst;
37 static gboolean parse_filter_option(const gchar *option_name,
38 const gchar *value, gpointer data, GError **error)
40 gchar **tokens;
41 unsigned int i;
42 uint16_t tmp;
43 gboolean invert;
45 (void)option_name;
46 (void)data;
47 (void)error;
49 if (!strlen(value))
50 return TRUE;
52 if (value[0] == '~') {
53 value++;
54 invert = TRUE;
55 } else {
56 invert = FALSE;
59 i = 0;
60 tokens = g_strsplit(value, ",", -1);
61 tmp = 0x0000;
63 while (tokens[i]) {
64 g_strstrip(tokens[i]);
66 if (!strlen(tokens[i])) {
67 i++;
68 continue;
71 if (!g_ascii_strcasecmp(tokens[i], "sync")) {
72 tmp |= (1 << LIBSWO_PACKET_TYPE_SYNC);
73 } else if (!g_ascii_strcasecmp(tokens[i], "of")) {
74 tmp |= (1 << LIBSWO_PACKET_TYPE_OVERFLOW);
75 } else if (!g_ascii_strcasecmp(tokens[i], "lts")) {
76 tmp |= (1 << LIBSWO_PACKET_TYPE_LTS);
77 } else if (!g_ascii_strcasecmp(tokens[i], "gts")) {
78 tmp |= (1 << LIBSWO_PACKET_TYPE_GTS1);
79 tmp |= (1 << LIBSWO_PACKET_TYPE_GTS2);
80 } else if (!g_ascii_strcasecmp(tokens[i], "gts1")) {
81 tmp |= (1 << LIBSWO_PACKET_TYPE_GTS1);
82 } else if (!g_ascii_strcasecmp(tokens[i], "gts2")) {
83 tmp |= (1 << LIBSWO_PACKET_TYPE_GTS2);
84 } else if (!g_ascii_strcasecmp(tokens[i], "ext")) {
85 tmp |= (1 << LIBSWO_PACKET_TYPE_EXT);
86 } else if (!g_ascii_strcasecmp(tokens[i], "inst")) {
87 tmp |= (1 << LIBSWO_PACKET_TYPE_INST);
88 } else if (!g_ascii_strcasecmp(tokens[i], "hw")) {
89 tmp |= (1 << LIBSWO_PACKET_TYPE_HW);
90 } else if (!g_ascii_strcasecmp(tokens[i], "evcnt")) {
91 tmp |= (1 << DWT_PACKET_TYPE_EVENT_COUNTER);
92 } else if (!g_ascii_strcasecmp(tokens[i], "exc")) {
93 tmp |= (1 << DWT_PACKET_TYPE_EXCEPTION_TRACE);
94 } else if (!g_ascii_strcasecmp(tokens[i], "pc")) {
95 tmp |= (1 << DWT_PACKET_TYPE_PC_SAMPLE);
96 } else if (!g_ascii_strcasecmp(tokens[i], "dtpc")) {
97 tmp |= (1 << DWT_PACKET_TYPE_DT_PC_VALUE);
98 } else if (!g_ascii_strcasecmp(tokens[i], "dtaddr")) {
99 tmp |= (1 << DWT_PACKET_TYPE_DT_ADDR_OFFSET);
100 } else if (!g_ascii_strcasecmp(tokens[i], "dtval")) {
101 tmp |= (1 << DWT_PACKET_TYPE_DT_DATA_VALUE);
102 } else if (!g_ascii_strcasecmp(tokens[i], "unknown")) {
103 tmp |= (1 << LIBSWO_PACKET_TYPE_UNKNOWN);
104 } else {
105 g_critical("Invalid packet type: %s.", tokens[i]);
106 g_strfreev(tokens);
107 return FALSE;
110 i++;
113 if (invert)
114 tmp = ~tmp;
117 * Apply the packet type filter only if at least one valid packet type
118 * was specified.
120 if (tmp > 0)
121 packet_type_filter = tmp;
123 g_strfreev(tokens);
125 return TRUE;
128 static gboolean parse_inst_filter_option(const gchar *option_name,
129 const gchar *value, gpointer data, GError **error)
131 gchar **tokens;
132 unsigned int i;
133 uint32_t tmp;
134 long int address;
135 char *endptr;
136 gboolean invert;
138 (void)option_name;
139 (void)data;
140 (void)error;
142 if (!strlen(value))
143 return TRUE;
145 if (value[0] == '~') {
146 value++;
147 invert = TRUE;
148 } else {
149 invert = FALSE;
152 i = 0;
153 tokens = g_strsplit(value, ",", -1);
154 tmp = 0x00000000;
156 while (tokens[i]) {
157 g_strstrip(tokens[i]);
159 if (!strlen(tokens[i])) {
160 i++;
161 continue;
164 address = strtoll(tokens[i], &endptr, 10);
166 if (endptr == tokens[i] || *endptr != '\0') {
167 g_critical("Invalid source address: %s.", tokens[i]);
168 g_strfreev(tokens);
169 return FALSE;
172 if (address < 0 || address > 31) {
173 g_critical("Source address out of range: %li.",
174 address);
175 g_strfreev(tokens);
176 return FALSE;
179 tmp |= (1 << address);
180 i++;
183 if (invert)
184 tmp = ~tmp;
187 * Apply the instrumentation source address filter only if at least one
188 * valid source address was specified.
190 if (tmp > 0)
191 inst_address_filter = tmp;
193 g_strfreev(tokens);
195 return TRUE;
198 static GOptionEntry entries[] = {
199 {"version", 'V', 0, G_OPTION_ARG_NONE, &opt_version,
200 "Show version information", NULL},
201 {"input-file", 'i', 0, G_OPTION_ARG_FILENAME, &input_file,
202 "Load trace data from file", NULL},
203 {"filter", 'f', 0, G_OPTION_ARG_CALLBACK, &parse_filter_option,
204 "Filter for packet types", NULL},
205 {"filter-inst", 0, 0, G_OPTION_ARG_CALLBACK, &parse_inst_filter_option,
206 "Filter for instrumentation source addresses", NULL},
207 {"dump-inst", 0, 0, G_OPTION_ARG_NONE, &opt_dump_inst,
208 "Dump instrumentation payload", NULL},
209 {NULL, 0, 0, 0, NULL, NULL, NULL}
212 static void handle_hw_packet(const union libswo_packet *packet)
214 if (dwt_handle_packet(&packet->hw))
215 return;
217 if (!(packet_type_filter & (1 << LIBSWO_PACKET_TYPE_HW)))
218 return;
220 printf("Hardware source (address = %u, value = %x, size = %zu bytes)\n",
221 packet->hw.address, packet->hw.value, packet->hw.size - 1);
224 static void handle_inst_packet(const union libswo_packet *packet)
226 if (!(packet_type_filter & (1 << LIBSWO_PACKET_TYPE_INST)))
227 return;
229 if (!(inst_address_filter & (1 << packet->inst.address)))
230 return;
232 if (opt_dump_inst) {
233 fwrite(packet->inst.payload, packet->inst.size - 1, 1, stdout);
234 fflush(stdout);
235 return;
238 printf("Instrumentation (address = %u, value = %x, size = %zu bytes)\n",
239 packet->inst.address, packet->inst.value,
240 packet->inst.size - 1);
243 static void handle_overflow_packet(const union libswo_packet *packet)
245 if (!(packet_type_filter & (1 << LIBSWO_PACKET_TYPE_OVERFLOW)))
246 return;
248 (void)packet;
249 printf("Overflow\n");
252 static void handle_ext_packet(const union libswo_packet *packet)
254 const char *src;
256 if (!(packet_type_filter & (1 << LIBSWO_PACKET_TYPE_EXT)))
257 return;
259 switch (packet->ext.source) {
260 case LIBSWO_EXT_SRC_ITM:
261 src = "ITM";
262 break;
263 case LIBSWO_EXT_SRC_HW:
264 src = "HW";
265 break;
266 default:
267 g_warning("Extension packet with invalid source: %u.",
268 packet->ext.source);
269 return;
272 printf("Extension (source = %s, value = %x)\n", src,
273 packet->ext.value);
276 static void handle_unknown_packet(const union libswo_packet *packet)
278 if (!(packet_type_filter & (1 << LIBSWO_PACKET_TYPE_UNKNOWN)))
279 return;
281 printf("Unknown data (size = %zu bytes)\n", packet->unknown.size);
284 static void handle_sync_packet(const union libswo_packet *packet)
286 if (!(packet_type_filter & (1 << LIBSWO_PACKET_TYPE_SYNC)))
287 return;
289 if (packet->sync.size % 8)
290 printf("Synchronization (size = %zu bits)\n",
291 packet->sync.size);
292 else
293 printf("Synchronization (size = %zu bytes)\n",
294 packet->sync.size / 8);
297 static void handle_lts_packet(const union libswo_packet *packet)
299 const char *tc;
301 if (!(packet_type_filter & (1 << LIBSWO_PACKET_TYPE_LTS)))
302 return;
304 switch (packet->lts.relation) {
305 case LIBSWO_LTS_REL_SYNC:
306 tc = "synchronous";
307 break;
308 case LIBSWO_LTS_REL_TS:
309 tc = "timestamp delayed";
310 break;
311 case LIBSWO_LTS_REL_SRC:
312 tc = "data delayed";
313 break;
314 case LIBSWO_LTS_REL_BOTH:
315 tc = "data and timestamp delayed";
316 break;
317 default:
318 g_warning("Local timestamp packet with invalid relation: %u.",
319 packet->lts.relation);
320 return;
323 printf("Local timestamp (relation = %s, value = %x)\n", tc,
324 packet->lts.value);
327 static void handle_gts1_packet(const union libswo_packet *packet)
329 if (!(packet_type_filter & (1 << LIBSWO_PACKET_TYPE_GTS1)))
330 return;
332 printf("Global timestamp (GTS1) (wrap = %u, clkch = %u, value = %x)\n",
333 packet->gts1.wrap, packet->gts1.clkch, packet->gts1.value);
336 static void handle_gts2_packet(const union libswo_packet *packet)
338 if (!(packet_type_filter & (1 << LIBSWO_PACKET_TYPE_GTS2)))
339 return;
341 printf("Global timestamp (GTS2) (value = %x)\n", packet->gts2.value);
344 static int packet_cb(struct libswo_context *ctx,
345 const union libswo_packet *packet, void *user_data)
347 (void)ctx;
348 (void)user_data;
350 switch (packet->type) {
351 case LIBSWO_PACKET_TYPE_SYNC:
352 handle_sync_packet(packet);
353 break;
354 case LIBSWO_PACKET_TYPE_INST:
355 handle_inst_packet(packet);
356 break;
357 case LIBSWO_PACKET_TYPE_OVERFLOW:
358 handle_overflow_packet(packet);
359 break;
360 case LIBSWO_PACKET_TYPE_EXT:
361 handle_ext_packet(packet);
362 break;
363 case LIBSWO_PACKET_TYPE_LTS:
364 handle_lts_packet(packet);
365 break;
366 case LIBSWO_PACKET_TYPE_GTS1:
367 handle_gts1_packet(packet);
368 break;
369 case LIBSWO_PACKET_TYPE_GTS2:
370 handle_gts2_packet(packet);
371 break;
372 case LIBSWO_PACKET_TYPE_HW:
373 handle_hw_packet(packet);
374 break;
375 case LIBSWO_PACKET_TYPE_UNKNOWN:
376 handle_unknown_packet(packet);
377 break;
378 default:
379 g_warning("Invalid packet type: %u.", packet->type);
380 break;
383 return TRUE;
386 static void show_version(void)
388 printf("%s\n", PACKAGE_STRING);
389 printf("Using libswo %s\n", libswo_version_package_get_string());
392 static int parse_options(int *argc, char ***argv)
394 GError *error;
395 GOptionContext *context;
397 error = NULL;
399 context = g_option_context_new(NULL);
400 g_option_context_add_main_entries(context, entries, NULL);
402 if (!g_option_context_parse(context, argc, argv, &error)) {
403 g_critical("%s.", error->message);
404 g_error_free(error);
405 g_option_context_free(context);
406 return FALSE;
409 g_option_context_free(context);
411 return TRUE;
414 static void log_handler(const gchar *domain, GLogLevelFlags level,
415 const gchar *message, gpointer user_data)
417 (void)domain;
418 (void)level;
419 (void)user_data;
421 fprintf(stderr, "%s\n", message);
422 fflush(stderr);
425 int main(int argc, char **argv)
427 int ret;
428 struct libswo_context *ctx;
429 uint8_t buffer[BUFFER_SIZE];
430 GIOChannel *input;
431 GError *error;
432 GIOStatus iostat;
433 gsize num;
435 g_log_set_default_handler(&log_handler, NULL);
437 opt_version = FALSE;
438 opt_dump_inst = FALSE;
440 /* Disable packet filtering for all packet types by default. */
441 packet_type_filter = (1 << LIBSWO_PACKET_TYPE_SYNC) | \
442 (1 << LIBSWO_PACKET_TYPE_OVERFLOW) | \
443 (1 << LIBSWO_PACKET_TYPE_LTS) | \
444 (1 << LIBSWO_PACKET_TYPE_GTS1) | \
445 (1 << LIBSWO_PACKET_TYPE_GTS2) | \
446 (1 << LIBSWO_PACKET_TYPE_EXT) | \
447 (1 << LIBSWO_PACKET_TYPE_INST) | \
448 (1 << LIBSWO_PACKET_TYPE_HW) | \
449 (1 << LIBSWO_PACKET_TYPE_UNKNOWN) | \
450 (1 << DWT_PACKET_TYPE_EVENT_COUNTER) | \
451 (1 << DWT_PACKET_TYPE_EXCEPTION_TRACE) | \
452 (1 << DWT_PACKET_TYPE_PC_SAMPLE) | \
453 (1 << DWT_PACKET_TYPE_DT_PC_VALUE) | \
454 (1 << DWT_PACKET_TYPE_DT_ADDR_OFFSET) | \
455 (1 << DWT_PACKET_TYPE_DT_DATA_VALUE);
457 /* Disable instrumentation source address filtering by default. */
458 inst_address_filter = 0xffffffff;
460 if (!parse_options(&argc, &argv))
461 return EXIT_FAILURE;
463 if (opt_version) {
464 show_version();
465 return EXIT_SUCCESS;
468 if (opt_dump_inst)
469 packet_type_filter = (1 << LIBSWO_PACKET_TYPE_INST);
471 error = NULL;
473 if (input_file) {
474 input = g_io_channel_new_file(input_file, "r", &error);
476 if (!input) {
477 g_critical("%s: %s.", input_file, error->message);
478 g_error_free(error);
479 g_free(input_file);
480 return EXIT_FAILURE;
483 g_free(input_file);
484 } else {
485 input = g_io_channel_unix_new(STDIN_FILENO);
488 /* Set encoding to binary (default is UTF-8). */
489 iostat = g_io_channel_set_encoding(input, NULL, &error);
491 if (iostat != G_IO_STATUS_NORMAL) {
492 g_critical("%s.", error->message);
493 g_error_free(error);
494 g_io_channel_unref(input);
495 return EXIT_FAILURE;
498 g_io_channel_set_buffered(input, FALSE);
500 ret = libswo_init(&ctx, NULL, BUFFER_SIZE * 2);
502 if (ret != LIBSWO_OK) {
503 g_critical("libswo_init() failed: %s.",
504 libswo_strerror_name(ret));
505 g_io_channel_unref(input);
506 return EXIT_FAILURE;
509 ret = libswo_set_callback(ctx, &packet_cb, NULL);
511 if (ret != LIBSWO_OK) {
512 g_critical("libswo_set_callback() failed: %s.",
513 libswo_strerror_name(ret));
514 g_io_channel_unref(input);
515 libswo_exit(ctx);
516 return EXIT_FAILURE;
519 while (TRUE) {
520 iostat = g_io_channel_read_chars(input, (gchar *)buffer,
521 BUFFER_SIZE, &num, &error);
523 if (iostat == G_IO_STATUS_ERROR)
524 break;
526 ret = libswo_feed(ctx, buffer, num);
528 if (ret != LIBSWO_OK) {
529 g_critical("libswo_feed() failed: %s.",
530 libswo_strerror_name(ret));
531 g_io_channel_unref(input);
532 libswo_exit(ctx);
533 return EXIT_FAILURE;
536 ret = libswo_decode(ctx, 0);
538 if (ret != LIBSWO_OK) {
539 g_critical("libswo_decode() failed: %s.",
540 libswo_strerror_name(ret));
541 g_io_channel_unref(input);
542 libswo_exit(ctx);
543 return EXIT_FAILURE;
546 if (iostat == G_IO_STATUS_EOF)
547 break;
550 if (iostat == G_IO_STATUS_ERROR) {
551 g_critical("%s.", error->message);
552 g_error_free(error);
553 g_io_channel_unref(input);
554 libswo_exit(ctx);
555 return EXIT_FAILURE;
558 ret = libswo_decode(ctx, LIBSWO_DF_EOS);
560 if (ret != LIBSWO_OK) {
561 g_critical("libswo_decode() failed: %s.",
562 libswo_strerror_name(ret));
563 g_io_channel_unref(input);
564 libswo_exit(ctx);
565 return EXIT_FAILURE;
568 g_io_channel_unref(input);
569 libswo_exit(ctx);
571 return EXIT_SUCCESS;