summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenjamin Otte <otte@gnome.org>2008-12-13 20:41:03 +0100
committerBenjamin Otte <otte@gnome.org>2008-12-16 21:38:17 +0100
commit6636c183c3d8b9cd30fed39aeffdb5da90045627 (patch)
tree2f4d5a6e4f9a674625cf1c76b9cea1eae6f09866
parent7de8641576163e12e6adc7b3f178d81b3500ef66 (diff)
add a tool for rewriting strace outputs to test output
-rw-r--r--tools/Makefile.am7
-rw-r--r--tools/rtmp-trace.c399
2 files changed, 404 insertions, 2 deletions
diff --git a/tools/Makefile.am b/tools/Makefile.am
index bfaecf2e..0b155e29 100644
--- a/tools/Makefile.am
+++ b/tools/Makefile.am
@@ -1,6 +1,6 @@
-noinst_PROGRAMS = swfdec-extract dump crashfinder
+noinst_PROGRAMS = swfdec-extract dump crashfinder rtmp-trace
-crashfinder_CFLAGS = $(GLOBAL_CFLAGS) $(SWFDEC_CFLAGS)
+crashfinder_CFLAGS = $(GLOBAL_CFLAGS) $(SWFDEC_CFLAGS) $(CAIRO_CFLAGS)
crashfinder_LDFLAGS = $(SWFDEC_LIBS) $(CAIRO_LIBS)
crashfinder_SOURCES = crashfinder.c
@@ -9,3 +9,6 @@ dump_LDFLAGS = $(SWFDEC_LIBS) $(CAIRO_LIBS) $(PANGO_LIBS)
swfdec_extract_CFLAGS = $(GLOBAL_CFLAGS) $(SWFDEC_CFLAGS) $(CAIRO_CFLAGS)
swfdec_extract_LDFLAGS = $(SWFDEC_LIBS) $(CAIRO_LIBS)
+
+rtmp_trace_CFLAGS = $(GLOBAL_CFLAGS) $(SWFDEC_CFLAGS)
+rtmp_trace_LDFLAGS = $(SWFDEC_LIBS)
diff --git a/tools/rtmp-trace.c b/tools/rtmp-trace.c
new file mode 100644
index 00000000..26e5b18c
--- /dev/null
+++ b/tools/rtmp-trace.c
@@ -0,0 +1,399 @@
+/* Swfdec
+ * Copyright (C) 2008 Benjamin Otte <otte@gnome.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301 USA
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <swfdec/swfdec.h>
+#include <swfdec/swfdec_rtmp_header.h>
+#include <swfdec/swfdec_rtmp_packet.h>
+
+static char *
+swfdec_buffer_to_string (SwfdecBuffer *b)
+{
+ GString *string;
+ guint i;
+
+ g_return_val_if_fail (b != NULL, NULL);
+
+ string = g_string_new ("");
+ for (i = 0; i < b->length; i++) {
+ guchar c = (guchar) b->data[i];
+ switch (c) {
+ case '\"':
+ case '\'':
+ case '\\':
+ g_string_append_c (string, '\\');
+ g_string_append_c (string, (char) c);
+ break;
+ case '\f':
+ g_string_append_c (string, '\\');
+ g_string_append_c (string, 'f');
+ break;
+ case '\n':
+ g_string_append_c (string, '\\');
+ g_string_append_c (string, 'n');
+ break;
+ case '\r':
+ g_string_append_c (string, '\\');
+ g_string_append_c (string, 'r');
+ break;
+ case '\t':
+ g_string_append_c (string, '\\');
+ g_string_append_c (string, 't');
+ break;
+ case '\v':
+ g_string_append_c (string, '\\');
+ g_string_append_c (string, 'v');
+ break;
+ default:
+ if (g_ascii_isprint(c)) {
+ g_string_append_c (string, (char) c);
+ } else if (i < b->length - 1 && g_ascii_isdigit(b->data[i + 1])) {
+ g_string_append_printf (string, "\\%03o", (guint) c);
+ } else {
+ g_string_append_printf (string, "\\%o",(guint) c);
+ }
+ break;
+ }
+ }
+
+ return g_string_free (string, FALSE);
+}
+
+static SwfdecBuffer *
+swfdec_buffer_from_string (const char *s)
+{
+ SwfdecBuffer *buffer;
+ GString *string;
+
+ g_return_val_if_fail (s != NULL, NULL);
+
+ string = g_string_new ("");
+
+ while (*s) {
+ if (*s == '\\') {
+ s++;
+ switch (*s) {
+ case '"':
+ g_string_append_c (string, '"');
+ break;
+ case '\'':
+ g_string_append_c (string, '\'');
+ break;
+ case '\\':
+ g_string_append_c (string, '\\');
+ break;
+ case 'f':
+ g_string_append_c (string, '\f');
+ break;
+ case 'n':
+ g_string_append_c (string, '\n');
+ break;
+ case 'r':
+ g_string_append_c (string, '\r');
+ break;
+ case 't':
+ g_string_append_c (string, '\t');
+ break;
+ case 'v':
+ g_string_append_c (string, '\v');
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ {
+ guint val = s[0] - '0';
+ if (s[1] >= '0' && s[1] <= '7') {
+ s++;
+ val = 8 * val + s[0] - '0';
+ if (s[1] >= '0' && s[1] <= '7') {
+ s++;
+ val = 8 * val + s[0] - '0';
+ if (val >= 256) {
+ g_printerr ("Invalid octal escape sequence");
+ }
+ }
+ }
+ g_string_append_c (string, val);
+ }
+ break;
+ default:
+ g_printerr ("Stray \\ in string");
+ break;
+ }
+ } else {
+ g_string_append_c (string, *s);
+ }
+ s++;
+ }
+
+ buffer = swfdec_buffer_new_for_data ((guchar *) string->str, string->len);
+ g_string_free (string, FALSE);
+ return buffer;
+}
+
+typedef struct _RtmpState {
+ guint handshake;
+ SwfdecBufferQueue * send;
+ GHashTable * send_pending;
+ SwfdecBufferQueue * recv;
+ GHashTable * recv_pending;
+} RtmpState;
+
+static RtmpState *
+rtmp_state_new (void)
+{
+ RtmpState *state = g_slice_new0 (RtmpState);
+
+ state->handshake = 3;
+ state->send = swfdec_buffer_queue_new ();
+ state->send_pending = g_hash_table_new (g_direct_hash, g_direct_equal);
+ state->recv = swfdec_buffer_queue_new ();
+ state->recv_pending = g_hash_table_new (g_direct_hash, g_direct_equal);
+
+ return state;
+}
+
+static void
+rtmp_state_free (RtmpState *state)
+{
+ swfdec_buffer_queue_unref (state->send);
+ g_hash_table_destroy (state->send_pending);
+ swfdec_buffer_queue_unref (state->recv);
+ g_hash_table_destroy (state->recv_pending);
+ g_slice_free (RtmpState, state);
+}
+
+static void
+write_line (GPtrArray *lines, const char *type, SwfdecBuffer *buffer)
+{
+ char *s;
+
+ s = swfdec_buffer_to_string (buffer);
+ g_ptr_array_add (lines, g_strconcat (type, " ", s, NULL));
+ g_free (s);
+}
+
+static gboolean
+process_one_packet (GPtrArray *lines, const char *type,
+ GHashTable *lookup, SwfdecBufferQueue *queue, guint packet_size)
+{
+ SwfdecRtmpPacket *packet;
+ SwfdecRtmpHeader header = { 0, };
+ SwfdecBuffer *buffer;
+ SwfdecBits bits;
+ gsize header_size, i, remaining;
+
+ /* determine size of header */
+ buffer = swfdec_buffer_queue_peek (queue, 1);
+ if (buffer == NULL)
+ return FALSE;
+ header_size = swfdec_rtmp_header_peek_size (buffer->data[0]);
+ swfdec_buffer_unref (buffer);
+
+ /* read header */
+ buffer = swfdec_buffer_queue_peek (queue, header_size);
+ if (buffer == NULL)
+ return FALSE;
+ swfdec_bits_init (&bits, buffer);
+ i = swfdec_rtmp_header_peek_channel (&bits);
+ packet = g_hash_table_lookup (lookup, GUINT_TO_POINTER (i));
+ if (packet == NULL) {
+ packet = swfdec_rtmp_packet_new_empty ();
+ g_hash_table_insert (lookup, GUINT_TO_POINTER (i), packet);
+ } else {
+ swfdec_rtmp_header_copy (&header, &packet->header);
+ }
+ swfdec_rtmp_header_read (&header, &bits);
+ swfdec_buffer_unref (buffer);
+
+ /* read the data chunk */
+ remaining = GPOINTER_TO_SIZE (packet->buffer);
+ if (remaining) {
+ if (header_size >= 4) {
+ g_printerr ("not a continuation header, but old command not finished yet, dropping old command\n");
+ remaining = header.size;
+ }
+ } else {
+ remaining = header.size;
+ }
+ if (header_size + MIN (packet_size, remaining) > swfdec_buffer_queue_get_depth (queue))
+ return FALSE;
+
+ swfdec_rtmp_header_copy (&packet->header, &header);
+ if (remaining <= packet_size) {
+ packet->buffer = GSIZE_TO_POINTER (0);
+ } else {
+ packet->buffer = GSIZE_TO_POINTER (remaining - packet_size);
+ remaining = packet_size;
+ }
+
+ buffer = swfdec_buffer_queue_pull (queue, header_size + remaining);
+ g_assert (buffer);
+ write_line (lines, type, buffer);
+ return TRUE;
+}
+
+static void
+rtmp_process_send (RtmpState *state, GPtrArray *lines, SwfdecBuffer *buffer)
+{
+ swfdec_buffer_queue_push (state->send, buffer);
+
+ switch (state->handshake) {
+ case 0:
+ break;
+ case 1:
+ buffer = swfdec_buffer_queue_pull (state->send, 1536);
+ if (buffer == NULL)
+ return;
+ write_line (lines, "send", buffer);
+ state->handshake--;
+ break;
+ case 2:
+ g_printerr ("sent data when waiting for RTMP handshake reply?");
+ return;
+ case 3:
+ buffer = swfdec_buffer_queue_pull (state->send, 1537);
+ if (buffer) {
+ write_line (lines, "send", buffer);
+ state->handshake--;
+ }
+ return;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ while (process_one_packet (lines, "send", state->send_pending, state->send, 128))
+ ;
+}
+
+static void
+rtmp_process_recv (RtmpState *state, GPtrArray *lines, SwfdecBuffer *buffer)
+{
+ swfdec_buffer_queue_push (state->recv, buffer);
+
+ switch (state->handshake) {
+ case 0:
+ break;
+ case 1:
+ g_printerr ("received data when waiting for RTMP handshake data?");
+ return;
+ case 2:
+ buffer = swfdec_buffer_queue_pull (state->recv, 1536 * 2 + 1);
+ if (buffer) {
+ write_line (lines, "recv", buffer);
+ state->handshake--;
+ }
+ return;
+ case 3:
+ g_printerr ("received data when before sending?");
+ return;
+ default:
+ g_assert_not_reached ();
+ break;
+ }
+
+ while (process_one_packet (lines, "recv", state->recv_pending, state->recv, 128))
+ ;
+}
+
+static char **
+rtmp_process (char **lines)
+{
+ GPtrArray *result;
+ RtmpState *state;
+ guint i;
+
+ result = g_ptr_array_new ();
+ state = rtmp_state_new ();
+ for (i = 0; lines[i] != NULL; i++) {
+ if (g_str_has_prefix (lines[i], "send ")) {
+ SwfdecBuffer *buffer = swfdec_buffer_from_string (lines[i] + 5);
+ rtmp_process_send (state, result, buffer);
+ } else if (g_str_has_prefix (lines[i], "recv ")) {
+ SwfdecBuffer *buffer = swfdec_buffer_from_string (lines[i] + 5);
+ rtmp_process_recv (state, result, buffer);
+ } else if (lines[i][0]) {
+ g_printerr ("ignoring line %u: %s\n", i + 1, lines[i]);
+ }
+ }
+ g_strfreev (lines);
+ g_ptr_array_add (result, g_strdup ("")); /* so we end with a newline */
+ g_ptr_array_add (result, NULL);
+ rtmp_state_free (state);
+
+ return (char **) g_ptr_array_free (result, FALSE);
+}
+
+int
+main (int argc, char **argv)
+{
+ GError *error = NULL;
+ char *contents;
+ char **lines;
+ GOptionEntry options[] = {
+ { NULL }
+ };
+ GOptionContext *ctx;
+
+ ctx = g_option_context_new ("");
+ g_option_context_add_main_entries (ctx, options, "options");
+ g_option_context_parse (ctx, &argc, &argv, &error);
+ g_option_context_free (ctx);
+
+ if (error) {
+ g_printerr ("Error parsing command line arguments: %s\n", error->message);
+ g_error_free (error);
+ return 1;
+ }
+ if (argc < 2) {
+ g_printerr ("Usage: %s [OPTIONS] infile [outfile]\n", argv[0]);
+ return 1;
+ }
+ if (!g_file_get_contents (argv[1], &contents, NULL, &error)) {
+ g_printerr ("Error opening file: %s\n", error->message);
+ g_error_free (error);
+ return 1;
+ }
+
+ lines = g_strsplit (contents, "\n", -1);
+ g_free (contents);
+
+
+ lines = rtmp_process (lines);
+
+
+ contents = g_strjoinv ("\n", lines);
+ g_strfreev (lines);
+ if (!g_file_set_contents (argc > 2 ? argv[2] : argv[1], contents, -1, &error)) {
+ g_printerr ("Error saving file: %s\n", error->message);
+ g_error_free (error);
+ return 1;
+ }
+ return 0;
+}
+