drm/radeon: add new cs command stream dumping facilities
authorJerome Glisse <jglisse@redhat.com>
Thu, 8 Apr 2010 15:50:34 +0000 (17:50 +0200)
committerJerome Glisse <jglisse@redhat.com>
Thu, 8 Apr 2010 15:53:09 +0000 (17:53 +0200)
Dump command stream + associated bo into a binary file
which follow a similar design as json file. It allows
to intercept a command stream and replay it in a standalone
program (see radeondb tools).

radeon/Makefile.am
radeon/bof.c [new file with mode: 0644]
radeon/bof.h [new file with mode: 0644]
radeon/radeon_cs_gem.c

index de78232..03b5040 100644 (file)
@@ -39,7 +39,8 @@ libdrm_radeon_la_SOURCES = \
        radeon_cs_gem.c \
        radeon_cs_space.c \
        radeon_bo.c \
-       radeon_cs.c
+       radeon_cs.c \
+       bof.c
 
 libdrm_radeonincludedir = ${includedir}/libdrm
 libdrm_radeoninclude_HEADERS = \
diff --git a/radeon/bof.c b/radeon/bof.c
new file mode 100644 (file)
index 0000000..0598cc6
--- /dev/null
@@ -0,0 +1,477 @@
+/*
+ * Copyright 2010 Jerome Glisse <glisse@freedesktop.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *      Jerome Glisse
+ */
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include "bof.h"
+
+/*
+ * helpers
+ */
+static int bof_entry_grow(bof_t *bof)
+{
+       bof_t **array;
+
+       if (bof->array_size < bof->nentry)
+               return 0;
+       array = realloc(bof->array, (bof->nentry + 16) * sizeof(void*));
+       if (array == NULL)
+               return -ENOMEM;
+       bof->array = array;
+       bof->nentry += 16;
+       return 0;
+}
+
+/*
+ * object 
+ */
+bof_t *bof_object(void)
+{
+       bof_t *object;
+
+       object = calloc(1, sizeof(bof_t));
+       if (object == NULL)
+               return NULL;
+       object->refcount = 1;
+       object->type = BOF_TYPE_OBJECT;
+       object->size = 12;
+       return object;
+}
+
+bof_t *bof_object_get(bof_t *object, const char *keyname)
+{
+       unsigned i;
+
+       for (i = 0; i < object->array_size; i += 2) {
+               if (!strcmp(object->array[i]->value, keyname)) {
+                       return object->array[i + 1];
+               }
+       }
+       return NULL;
+}
+
+int bof_object_set(bof_t *object, const char *keyname, bof_t *value)
+{
+       bof_t *key;
+       int r;
+
+       if (object->type != BOF_TYPE_OBJECT)
+               return -EINVAL;
+       r = bof_entry_grow(object);
+       if (r)
+               return r;
+       key = bof_string(keyname);
+       if (key == NULL)
+               return -ENOMEM;
+       object->array[object->array_size++] = key;
+       object->array[object->array_size++] = value;
+       object->size += value->size;
+       object->size += key->size;
+       bof_incref(value);
+       return 0;
+}
+
+/*
+ * array
+ */
+bof_t *bof_array(void)
+{
+       bof_t *array = bof_object();
+
+       if (array == NULL)
+               return NULL;
+       array->type = BOF_TYPE_ARRAY;
+       array->size = 12;
+       return array;
+}
+
+int bof_array_append(bof_t *array, bof_t *value)
+{
+       int r;
+       if (array->type != BOF_TYPE_ARRAY)
+               return -EINVAL;
+       r = bof_entry_grow(array);
+       if (r)
+               return r;
+       array->array[array->array_size++] = value;
+       array->size += value->size;
+       bof_incref(value);
+       return 0;
+}
+
+bof_t *bof_array_get(bof_t *bof, unsigned i)
+{
+       if (!bof_is_array(bof) || i >= bof->array_size)
+               return NULL;
+       return bof->array[i];
+}
+
+unsigned bof_array_size(bof_t *bof)
+{
+       if (!bof_is_array(bof))
+               return 0;
+       return bof->array_size;
+}
+
+/*
+ * blob
+ */
+bof_t *bof_blob(unsigned size, void *value)
+{
+       bof_t *blob = bof_object();
+
+       if (blob == NULL)
+               return NULL;
+       blob->type = BOF_TYPE_BLOB;
+       blob->value = calloc(1, size);
+       if (blob->value == NULL) {
+               bof_decref(blob);
+               return NULL;
+       }
+       blob->size = size;
+       memcpy(blob->value, value, size);
+       blob->size += 12;
+       return blob;
+}
+
+unsigned bof_blob_size(bof_t *bof)
+{
+       if (!bof_is_blob(bof))
+               return 0;
+       return bof->size - 12;
+}
+
+void *bof_blob_value(bof_t *bof)
+{
+       if (!bof_is_blob(bof))
+               return NULL;
+       return bof->value;
+}
+
+/*
+ * string
+ */
+bof_t *bof_string(const char *value)
+{
+       bof_t *string = bof_object();
+
+       if (string == NULL)
+               return NULL;
+       string->type = BOF_TYPE_STRING;
+       string->size = strlen(value) + 1;
+       string->value = calloc(1, string->size);
+       if (string->value == NULL) {
+               bof_decref(string);
+               return NULL;
+       }
+       strcpy(string->value, value);
+       string->size += 12;
+       return string;
+}
+
+/*
+ *  int32
+ */
+bof_t *bof_int32(int32_t value)
+{
+       bof_t *int32 = bof_object();
+
+       if (int32 == NULL)
+               return NULL;
+       int32->type = BOF_TYPE_INT32;
+       int32->size = 4;
+       int32->value = calloc(1, int32->size);
+       if (int32->value == NULL) {
+               bof_decref(int32);
+               return NULL;
+       }
+       memcpy(int32->value, &value, 4);
+       int32->size += 12;
+       return int32;
+}
+
+int32_t bof_int32_value(bof_t *bof)
+{
+       return *((uint32_t*)bof->value);
+}
+
+/*
+ *  common
+ */
+static void bof_indent(int level)
+{
+       int i;
+
+       for (i = 0; i < level; i++)
+               fprintf(stderr, " ");
+}
+
+static void bof_print_bof(bof_t *bof, int level, int entry)
+{
+       bof_indent(level);
+       if (bof == NULL) {
+               fprintf(stderr, "--NULL-- for entry %d\n", entry);
+               return;
+       }
+       switch (bof->type) {
+       case BOF_TYPE_STRING:
+               fprintf(stderr, "%p string [%s %d]\n", bof, (char*)bof->value, bof->size);
+               break;
+       case BOF_TYPE_INT32:
+               fprintf(stderr, "%p int32 [%d %d]\n", bof, *(int*)bof->value, bof->size);
+               break;
+       case BOF_TYPE_BLOB:
+               fprintf(stderr, "%p blob [%d]\n", bof, bof->size);
+               break;
+       case BOF_TYPE_NULL:
+               fprintf(stderr, "%p null [%d]\n", bof, bof->size);
+               break;
+       case BOF_TYPE_OBJECT:
+               fprintf(stderr, "%p object [%d %d]\n", bof, bof->array_size / 2, bof->size);
+               break;
+       case BOF_TYPE_ARRAY:
+               fprintf(stderr, "%p array [%d %d]\n", bof, bof->array_size, bof->size);
+               break;
+       default:
+               fprintf(stderr, "%p unknown [%d]\n", bof, bof->type);
+               return;
+       }
+}
+
+static void bof_print_rec(bof_t *bof, int level, int entry)
+{
+       unsigned i;
+
+       bof_print_bof(bof, level, entry);
+       for (i = 0; i < bof->array_size; i++) {
+               bof_print_rec(bof->array[i], level + 2, i);
+       }
+}
+
+void bof_print(bof_t *bof)
+{
+       bof_print_rec(bof, 0, 0);
+}
+
+static int bof_read(bof_t *root, FILE *file, long end, int level)
+{
+       bof_t *bof = NULL;
+       int r;
+
+       if (ftell(file) >= end) {
+               return 0;
+       }
+       r = bof_entry_grow(root);
+       if (r)
+               return r;
+       bof = bof_object();
+       if (bof == NULL)
+               return -ENOMEM;
+       bof->offset = ftell(file);
+       r = fread(&bof->type, 4, 1, file);
+       if (r != 1)
+               goto out_err;
+       r = fread(&bof->size, 4, 1, file);
+       if (r != 1)
+               goto out_err;
+       r = fread(&bof->array_size, 4, 1, file);
+       if (r != 1)
+               goto out_err;
+       switch (bof->type) {
+       case BOF_TYPE_STRING:
+       case BOF_TYPE_INT32:
+       case BOF_TYPE_BLOB:
+               bof->value = calloc(1, bof->size - 12);
+               if (bof->value == NULL) {
+                       goto out_err;
+               }
+               r = fread(bof->value, bof->size - 12, 1, file);
+               if (r != 1) {
+                       fprintf(stderr, "error reading %d\n", bof->size - 12);
+                       goto out_err;
+               }
+               break;
+       case BOF_TYPE_NULL:
+               return 0;
+       case BOF_TYPE_OBJECT:
+       case BOF_TYPE_ARRAY:
+               r = bof_read(bof, file, bof->offset + bof->size, level + 2);
+               if (r)
+                       goto out_err;
+               break;
+       default:
+               fprintf(stderr, "invalid type %d\n", bof->type);
+               goto out_err;
+       }
+       root->array[root->centry++] = bof;
+       return bof_read(root, file, end, level);
+out_err:
+       bof_decref(bof);
+       return -EINVAL;
+}
+
+bof_t *bof_load_file(const char *filename)
+{
+       bof_t *root = bof_object();
+       int r;
+
+       if (root == NULL) {
+               fprintf(stderr, "%s failed to create root object\n", __func__);
+               return NULL;
+       }
+       root->file = fopen(filename, "r");
+       if (root->file == NULL)
+               goto out_err;
+       r = fseek(root->file, 0L, SEEK_SET);
+       if (r) {
+               fprintf(stderr, "%s failed to seek into file %s\n", __func__, filename);
+               goto out_err;
+       }
+       root->offset = ftell(root->file);
+       r = fread(&root->type, 4, 1, root->file);
+       if (r != 1)
+               goto out_err;
+       r = fread(&root->size, 4, 1, root->file);
+       if (r != 1)
+               goto out_err;
+       r = fread(&root->array_size, 4, 1, root->file);
+       if (r != 1)
+               goto out_err;
+       r = bof_read(root, root->file, root->offset + root->size, 2);
+       if (r)
+               goto out_err;
+       return root;
+out_err:
+       bof_decref(root);
+       return NULL;
+}
+
+void bof_incref(bof_t *bof)
+{
+       bof->refcount++;
+}
+
+void bof_decref(bof_t *bof)
+{
+       unsigned i;
+
+       if (bof == NULL)
+               return;
+       if (--bof->refcount > 0)
+               return;
+       for (i = 0; i < bof->array_size; i++) {
+               bof_decref(bof->array[i]);
+               bof->array[i] = NULL;
+       }
+       bof->array_size = 0;
+       if (bof->file) {
+               fclose(bof->file);
+               bof->file = NULL;
+       }
+       free(bof->array);
+       free(bof->value);
+       free(bof);
+}
+
+static int bof_file_write(bof_t *bof, FILE *file)
+{
+       unsigned i;
+       int r;
+
+       r = fwrite(&bof->type, 4, 1, file);
+       if (r != 1)
+               return -EINVAL;
+       r = fwrite(&bof->size, 4, 1, file);
+       if (r != 1)
+               return -EINVAL;
+       r = fwrite(&bof->array_size, 4, 1, file);
+       if (r != 1)
+               return -EINVAL;
+       switch (bof->type) {
+       case BOF_TYPE_NULL:
+               if (bof->size)
+                       return -EINVAL;
+               break;
+       case BOF_TYPE_STRING:
+       case BOF_TYPE_INT32:
+       case BOF_TYPE_BLOB:
+               r = fwrite(bof->value, bof->size - 12, 1, file);
+               if (r != 1)
+                       return -EINVAL;
+               break;
+       case BOF_TYPE_OBJECT:
+       case BOF_TYPE_ARRAY:
+               for (i = 0; i < bof->array_size; i++) {
+                       r = bof_file_write(bof->array[i], file);
+                       if (r)
+                               return r;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
+int bof_dump_file(bof_t *bof, const char *filename)
+{
+       unsigned i;
+       int r = 0;
+
+       if (bof->file) {
+               fclose(bof->file);
+               bof->file = NULL;
+       }
+       bof->file = fopen(filename, "w");
+       if (bof->file == NULL) {
+               fprintf(stderr, "%s failed to open file %s\n", __func__, filename);
+               r = -EINVAL;
+               goto out_err;
+       }
+       r = fseek(bof->file, 0L, SEEK_SET);
+       if (r) {
+               fprintf(stderr, "%s failed to seek into file %s\n", __func__, filename);
+               goto out_err;
+       }
+       r = fwrite(&bof->type, 4, 1, bof->file);
+       if (r != 1)
+               goto out_err;
+       r = fwrite(&bof->size, 4, 1, bof->file);
+       if (r != 1)
+               goto out_err;
+       r = fwrite(&bof->array_size, 4, 1, bof->file);
+       if (r != 1)
+               goto out_err;
+       for (i = 0; i < bof->array_size; i++) {
+               r = bof_file_write(bof->array[i], bof->file);
+               if (r)
+                       return r;
+       }
+out_err:
+       fclose(bof->file);
+       bof->file = NULL;
+       return r;
+}
diff --git a/radeon/bof.h b/radeon/bof.h
new file mode 100644 (file)
index 0000000..014affb
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2010 Jerome Glisse <glisse@freedesktop.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * on the rights to use, copy, modify, merge, publish, distribute, sub
+ * license, and/or sell copies of the Software, and to permit persons to whom
+ * the Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+ * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+ * USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Authors:
+ *      Jerome Glisse
+ */
+#ifndef BOF_H
+#define BOF_H
+
+#include <stdio.h>
+#include <stdint.h>
+
+#define BOF_TYPE_STRING                0
+#define BOF_TYPE_NULL          1
+#define BOF_TYPE_BLOB          2
+#define BOF_TYPE_OBJECT                3
+#define BOF_TYPE_ARRAY         4
+#define BOF_TYPE_INT32         5
+
+struct bof;
+
+typedef struct bof {
+       struct bof      **array;
+       unsigned        centry;
+       unsigned        nentry;
+       unsigned        refcount;
+       FILE            *file;
+       uint32_t        type;
+       uint32_t        size;
+       uint32_t        array_size;
+       void            *value;
+       long            offset;
+} bof_t;
+
+extern int bof_file_flush(bof_t *root);
+extern bof_t *bof_file_new(const char *filename);
+extern int bof_object_dump(bof_t *object, const char *filename);
+
+/* object */
+extern bof_t *bof_object(void);
+extern bof_t *bof_object_get(bof_t *object, const char *keyname);
+extern int bof_object_set(bof_t *object, const char *keyname, bof_t *value);
+/* array */
+extern bof_t *bof_array(void);
+extern int bof_array_append(bof_t *array, bof_t *value);
+extern bof_t *bof_array_get(bof_t *bof, unsigned i);
+extern unsigned bof_array_size(bof_t *bof);
+/* blob */
+extern bof_t *bof_blob(unsigned size, void *value);
+extern unsigned bof_blob_size(bof_t *bof);
+extern void *bof_blob_value(bof_t *bof);
+/* string */
+extern bof_t *bof_string(const char *value);
+/* int32 */
+extern bof_t *bof_int32(int32_t value);
+extern int32_t bof_int32_value(bof_t *bof);
+/* common functions */
+extern void bof_decref(bof_t *bof);
+extern void bof_incref(bof_t *bof);
+extern bof_t *bof_load_file(const char *filename);
+extern int bof_dump_file(bof_t *bof, const char *filename);
+extern void bof_print(bof_t *bof);
+
+static inline int bof_is_object(bof_t *bof){return (bof->type == BOF_TYPE_OBJECT);}
+static inline int bof_is_blob(bof_t *bof){return (bof->type == BOF_TYPE_BLOB);}
+static inline int bof_is_null(bof_t *bof){return (bof->type == BOF_TYPE_NULL);}
+static inline int bof_is_int32(bof_t *bof){return (bof->type == BOF_TYPE_INT32);}
+static inline int bof_is_array(bof_t *bof){return (bof->type == BOF_TYPE_ARRAY);}
+static inline int bof_is_string(bof_t *bof){return (bof->type == BOF_TYPE_STRING);}
+
+#endif
index 7f4d3be..81bd393 100644 (file)
@@ -32,6 +32,7 @@
 #include <assert.h>
 #include <errno.h>
 #include <stdlib.h>
+#include <string.h>
 #include <pthread.h>
 #include <sys/mman.h>
 #include <sys/ioctl.h>
 #include "xf86drm.h"
 #include "xf86atomic.h"
 #include "radeon_drm.h"
+#include "bof.h"
+
+#define CS_BOF_DUMP 0
 
 struct radeon_cs_manager_gem {
-    struct radeon_cs_manager base;
-    uint32_t                 device_id;
+    struct radeon_cs_manager    base;
+    uint32_t                    device_id;
+    unsigned                    nbof;
 };
 
 #pragma pack(1)
@@ -62,7 +67,7 @@ struct cs_reloc_gem {
 #define RELOC_SIZE (sizeof(struct cs_reloc_gem) / sizeof(uint32_t))
 
 struct cs_gem {
-    struct radeon_cs_int            base;
+    struct radeon_cs_int        base;
     struct drm_radeon_cs        cs;
     struct drm_radeon_cs_chunk  chunks[2];
     unsigned                    nrelocs;
@@ -325,6 +330,92 @@ static int cs_gem_end(struct radeon_cs_int *cs,
     return 0;
 }
 
+static void cs_gem_dump_bof(struct radeon_cs_int *cs)
+{
+    struct cs_gem *csg = (struct cs_gem*)cs;
+    struct radeon_cs_manager_gem *csm;
+    bof_t *bcs, *blob, *array, *bo, *size, *handle, *device_id, *root;
+    char tmp[256];
+    unsigned i;
+
+    csm = (struct radeon_cs_manager_gem *)cs->csm;
+    root = device_id = bcs = blob = array = bo = size = handle = NULL;
+    root = bof_object();
+    if (root == NULL)
+        goto out_err;
+    device_id = bof_int32(csm->device_id);
+    if (device_id == NULL)
+        return;
+    if (bof_object_set(root, "device_id", device_id))
+        goto out_err;
+    bof_decref(device_id);
+    device_id = NULL;
+    /* dump relocs */
+    blob = bof_blob(csg->nrelocs * 16, csg->relocs);
+    if (blob == NULL)
+        goto out_err;
+    if (bof_object_set(root, "reloc", blob))
+        goto out_err;
+    bof_decref(blob);
+    blob = NULL;
+    /* dump cs */
+    blob = bof_blob(cs->cdw * 4, cs->packets);
+    if (blob == NULL)
+        goto out_err;
+    if (bof_object_set(root, "pm4", blob))
+        goto out_err;
+    bof_decref(blob);
+    blob = NULL;
+    /* dump bo */
+    array = bof_array();
+    if (array == NULL)
+        goto out_err;
+    for (i = 0; i < csg->base.crelocs; i++) {
+        bo = bof_object();
+        if (bo == NULL)
+            goto out_err;
+        size = bof_int32(csg->relocs_bo[i]->size);
+        if (size == NULL)
+            goto out_err;
+        if (bof_object_set(bo, "size", size))
+            goto out_err;
+        bof_decref(size);
+        size = NULL;
+        handle = bof_int32(csg->relocs_bo[i]->handle);
+        if (handle == NULL)
+            goto out_err;
+        if (bof_object_set(bo, "handle", handle))
+            goto out_err;
+        bof_decref(handle);
+        handle = NULL;
+        radeon_bo_map((struct radeon_bo*)csg->relocs_bo[i], 0);
+        blob = bof_blob(csg->relocs_bo[i]->size, csg->relocs_bo[i]->ptr);
+        radeon_bo_unmap((struct radeon_bo*)csg->relocs_bo[i]);
+        if (blob == NULL)
+            goto out_err;
+        if (bof_object_set(bo, "data", blob))
+            goto out_err;
+        bof_decref(blob);
+        blob = NULL;
+        if (bof_array_append(array, bo))
+            goto out_err;
+        bof_decref(bo);
+        bo = NULL;
+    }
+    if (bof_object_set(root, "bo", array))
+        goto out_err;
+    sprintf(tmp, "d-0x%04X-%08d.bof", csm->device_id, csm->nbof++);
+    bof_dump_file(root, tmp);
+out_err:
+    bof_decref(blob);
+    bof_decref(array);
+    bof_decref(bo);
+    bof_decref(size);
+    bof_decref(handle);
+    bof_decref(device_id);
+    bof_decref(root);
+}
+
 static int cs_gem_emit(struct radeon_cs_int *cs)
 {
     struct cs_gem *csg = (struct cs_gem*)cs;
@@ -332,6 +423,9 @@ static int cs_gem_emit(struct radeon_cs_int *cs)
     unsigned i;
     int r;
 
+#if CS_BOF_DUMP
+    cs_gem_dump_bof(cs);
+#endif
     csg->chunks[0].length_dw = cs->cdw;
 
     chunk_array[0] = (uint64_t)(uintptr_t)&csg->chunks[0];
@@ -428,7 +522,7 @@ static int radeon_get_device_id(int fd, uint32_t *device_id)
 
     *device_id = 0;
     info.request = RADEON_INFO_DEVICE_ID;
-    info.value = device_id;
+    info.value = (uintptr_t)device_id;
     r = drmCommandWriteRead(fd, DRM_RADEON_INFO, &info,
                             sizeof(struct drm_radeon_info));
     return r;