Extract more symbolic information from the tree.
[panamaz] / src / export.cc
index ed19501..17d4dad 100644 (file)
@@ -2,7 +2,7 @@
  * export c types and prototypes to perl file.
  *
  * Copyright (c) 2019 Yonatan Goldschmidt
- * Copyright (c) 2020 Michael Zucchi
+ * Copyright (c) 2020,2021 Michael Zucchi
  *
  * The MIT License (MIT)
  *
@@ -50,12 +50,15 @@ FUNCTION_DECL     DECL_ARGUENTS    TREE_TYPE(item):PARM_DECL   DECL_NAME(item)
 #include <gcc-plugin.h>
 #include <tree.h>
 #include <print-tree.h>
+#include <tree-pretty-print.h>
 
-#include "tree-codes.h"
 #include "list.h"
 
 #define D(x) do { x; } while(0)
 
+extern const char *tree_codes[];
+#define ZTREE_CODE(x) tree_codes[TREE_CODE(x)]
+
 //Enums have a type, otherwise they're just integers
 //#define TYPED_ENUMS
 
@@ -65,24 +68,30 @@ FUNCTION_DECL     DECL_ARGUENTS    TREE_TYPE(item):PARM_DECL   DECL_NAME(item)
 int plugin_is_GPL_compatible; // must be defined for the plugin to run
 
 static FILE *output_file;
+static int debug_level = 0;
 
 static void debug_tree_helper(tree t, const char *msg) {
 #ifndef NDEBUG
-       fflush(stdout);
-       fprintf(stderr, "dumping tree: '%s'\n", msg);
-       debug_tree(t);
-       fprintf(stderr, "\n\n");
-       fflush(stdout);
+       if (debug_level > 2) {
+               fflush(stdout);
+               fprintf(stderr, "dumping tree: '%s'\n", msg);
+               debug_tree(t);
+               fprintf(stderr, "\n\n");
+               fflush(stdout);
+       }
 #endif
 }
 
 static struct hash dumped;
 static struct list todump;
 static struct list context_stack;
+static struct list parameters; // last list of params
+
+static struct hash forward_types;
 
 /*
   Join all names in the stack, in reverse order.
- */
+*/
 static char *stack_path(struct list *stack, const char *sep) {
        size_t total = 1;
 
@@ -102,14 +111,24 @@ static char *stack_path(struct list *stack, const char *sep) {
        return data;
 }
 
+static void list_clear(struct list *list) {
+       struct node *node;
+
+       while ((node = list_remhead(list))) {
+               if (debug_level > 2)
+                       fprintf(stderr, " free: %s\n", node->name);
+               free(node);
+       }
+}
+
 // returns 0 if type has no size (i.e VOID_TYPE)
 static bool is_struct_or_union(const_tree type) {
-    return RECORD_TYPE == TREE_CODE(type) || UNION_TYPE == TREE_CODE(type);
+       return RECORD_TYPE == TREE_CODE(type) || UNION_TYPE == TREE_CODE(type);
 }
 
 static void print_spaces(int n) {
-    for (int i = 0; i < n; ++i)
-        fputc('\t', output_file);
+       for (int i = 0; i < n; ++i)
+               fputc('\t', output_file);
 }
 
 static int is_ref_type(tree type) {
@@ -154,7 +173,7 @@ static const char *value_name(tree type) {
 
 /*
   Find a non-ref type in the type chain, i.e. skip pointers/arrays.
- */
+*/
 static tree simple_type(tree t) {
        while (is_ref_type(t))
                t = TREE_TYPE(t);
@@ -163,7 +182,7 @@ static tree simple_type(tree t) {
 
 /*
   Create a 'panama' signature for a single type.
- */
+*/
 static void export_desc(tree field, tree field_type, struct buffer *b) {
        const size_t data_size = value_size(TYPE_SIZE(field_type));
 
@@ -221,8 +240,123 @@ static void export_desc(tree field, tree field_type, struct buffer *b) {
 #else
                buffer_room(b, 16);
                b->pos += sprintf(b->data + b->pos, "%c%zu",
-                                 TYPE_UNSIGNED(field_type) ? 'u' : 'i',
-                                 data_size);
+                       TYPE_UNSIGNED(field_type) ? 'u' : 'i',
+                       data_size);
+#endif
+               break;
+       case RECORD_TYPE:
+       case UNION_TYPE:
+               if (TYPE_IDENTIFIER(field_type)) {
+                       list_add(&todump, node_alloc(field_type, NULL));
+
+                       buffer_add(b, "${");
+                       buffer_add(b, value_name(field_type));
+                       buffer_add(b, "}");
+               } else {
+                       char *name = stack_path(&context_stack, "_");
+
+                       list_add(&todump, node_alloc(field_type, name));
+
+                       buffer_add(b, "${");
+                       buffer_add(b, name);
+                       buffer_add(b, "}");
+
+                       free(name);
+               }
+               break;
+       case REAL_TYPE:
+               buffer_room(b, 16);
+               b->pos += sprintf(b->data + b->pos, "f%zu", data_size);
+               break;
+       case INTEGER_TYPE:
+               buffer_room(b, 16);
+               b->pos += sprintf(b->data + b->pos, "%c%zu",
+                       TYPE_UNSIGNED(field_type) ? 'u' : 'i',
+                       data_size);
+               break;
+       default:
+               debug_tree_helper(field_type, "unknown type!");
+               gcc_unreachable();
+               break;
+       }
+}
+
+/*
+  Create a 'c' description of type.
+*/
+static void export_cdesc(tree field, tree field_type, struct buffer *b) {
+       const size_t data_size = value_size(TYPE_SIZE(field_type));
+
+       switch (TREE_CODE(field_type)) {
+       case VOID_TYPE:
+               buffer_add(b, "void");
+               break;
+       case VECTOR_TYPE:
+       case ARRAY_TYPE: {
+               const size_t elem_size = tree_to_uhwi(TYPE_SIZE_UNIT(TREE_TYPE(field_type)));
+               size_t num_elem;
+
+               if (elem_size == 0 || NULL == TYPE_SIZE_UNIT(field_type)) {
+                       // it is a flexible array or empty types
+                       num_elem = 0;
+               } else {
+                       // it might be 0 / elem_size, in which case we also end up with num_elem = 0.
+                       num_elem = tree_to_uhwi(TYPE_SIZE_UNIT(field_type)) / elem_size;
+               }
+
+               export_cdesc(field, TREE_TYPE(field_type), b);
+
+               buffer_room(b, 16);
+               buffer_add(b, "[");
+               if (num_elem)
+                       b->pos += sprintf(b->data + b->pos, "%zu", num_elem);
+               buffer_add(b, "]");
+               break;
+       }
+       case POINTER_TYPE:
+       case REFERENCE_TYPE:
+               buffer_room(b, 16);
+               buffer_add(b, "*");
+               //b->pos += sprintf(b->data + b->pos, "u%zu:", data_size);
+               export_cdesc(field, TREE_TYPE(field_type), b);
+               break;
+       case FUNCTION_TYPE: {
+               // TODO: handle void f() type -> null TYPE_ARG_TYPES()
+               tree return_type = TREE_TYPE(field_type);
+               int args = 0;
+
+               export_cdesc(field, return_type, b);
+
+               buffer_add(b, "(*)");
+               buffer_add(b, "(");
+               for (tree param = TYPE_ARG_TYPES(field_type); param != NULL; param = TREE_CHAIN(param)) {
+                       tree param_type = TREE_VALUE(param);
+
+                       // TREE_TYPE might? point to a type that a previous decl has also referenced
+
+                       if (args++)
+                               buffer_add(b, ", ");
+
+                       if (TREE_CODE(param_type) == VOID_TYPE) {
+                               buffer_add(b, "void");
+                               break;
+                       }
+
+                       export_cdesc(param, param_type, b);
+               }
+               buffer_add(b, ")");
+               break;
+       }
+       case ENUMERAL_TYPE:
+#if defined(TYPED_ENUMS)
+               buffer_add(b, "${");
+               buffer_add(b, TYPE_IDENTIFIER(field_type) ? value_name(field_type) : "enum");
+               buffer_add(b, "}");
+#else
+               buffer_room(b, 16);
+               b->pos += sprintf(b->data + b->pos, "%c%zu",
+                       TYPE_UNSIGNED(field_type) ? 'u' : 'i',
+                       data_size);
 #endif
                break;
        case RECORD_TYPE:
@@ -252,8 +386,8 @@ static void export_desc(tree field, tree field_type, struct buffer *b) {
        case INTEGER_TYPE:
                buffer_room(b, 16);
                b->pos += sprintf(b->data + b->pos, "%c%zu",
-                                 TYPE_UNSIGNED(field_type) ? 'u' : 'i',
-                                 data_size);
+                       TYPE_UNSIGNED(field_type) ? 'u' : 'i',
+                       data_size);
                break;
        default:
                debug_tree_helper(field_type, "unknown type!");
@@ -264,7 +398,7 @@ static void export_desc(tree field, tree field_type, struct buffer *b) {
 
 /*
   Print a single parameter or field.
- */
+*/
 static void export_param(tree field, tree field_type, size_t field_size) {
        switch (TREE_CODE(field_type)) {
        case VECTOR_TYPE:
@@ -286,6 +420,7 @@ static void export_param(tree field, tree field_type, size_t field_size) {
        }
        case VOID_TYPE:
                fprintf(output_file, " type => 'void',");
+               fprintf(output_file, " ctype => 'void',");
                break;
        case ENUMERAL_TYPE: {
 #if defined(TYPED_ENUMS)
@@ -294,24 +429,26 @@ static void export_param(tree field, tree field_type, size_t field_size) {
 #else
                fprintf(output_file, " type => '%c%zu',", TYPE_UNSIGNED(field_type) ? 'u' : 'i', field_size);
 #endif
+               fprintf(output_file, " ctype => 'enum %s',", value_name(field_type));
                break;
        }
        case FUNCTION_TYPE: {
-               // If the function is a typedef we could use the name
-               // but it's a pain to find up the tree, so don't bother.
-               // Use the signature instead.  This is what jextract appears to use.
                struct buffer b;
+               tree root_type = TREE_TYPE(field);
 
-               //char *name = stack_path(&context_stack);
-               //fprintf(output_file, " type => 'call:%s', ", name);
-               //free(name);
+               // If this is a typedef we might have a name for the type, otherwise it's a signature based name
+               if (root_type && TYPE_IDENTIFIER(root_type)) {
+                       fprintf(output_file, " type => 'call:%s', ", value_name(root_type));
+               } else {
+                       fprintf(stderr, "save for later param type %p\n", field_type);
+                       buffer_init(&b, 256);
+                       export_desc(field, field_type, &b);
+                       list_add(&todump, node_alloc(field_type, b.data));
+                       fprintf(output_file, " type => 'call:%s', ", b.data);
+               }
 
                buffer_init(&b, 256);
-
-               export_desc(field, field_type, &b);
-               list_add(&todump, node_alloc(field_type, b.data));
-               fprintf(output_file, " type => 'call:%s', ", b.data);
-
+               export_cdesc(field, field_type, &b);
                free(b.data);
 
                break;
@@ -345,7 +482,7 @@ static void export_param(tree field, tree field_type, size_t field_size) {
                break;
        }
        default:
-               printf("unknown param type: %s\n", ZTREE_CODE(field_type));
+               fprintf(stderr, "unknown param type: %s\n", ZTREE_CODE(field_type));
                gcc_unreachable();
        }
 }
@@ -353,14 +490,42 @@ static void export_param(tree field, tree field_type, size_t field_size) {
 /*
   Export a chain of parameters
 */
-static void export_params(tree first_param, size_t indent_level) {
+static void export_params(tree func) {
        int id = 0;
-       char nameb[16];
-       const char *names = nameb;
+       char nameb[32];
+       struct list args = { 0 };
+       struct node *name;
+       struct node *fwd = hash_lookup_bytype(&forward_types, func);
+
+       if (fwd) {
+               // use the forward reference to find the names
+               fprintf(stderr, "found forward reference @ %p\n", fwd);
+               name = fwd->list.head;
+       } else {
+               // paramter names are in the paramters list
+               // but they are in reverse order
+               int id = 0;
+               for (tree param = TYPE_ARG_TYPES(func); param; param = TREE_CHAIN(param)) {
+                       tree param_type = TREE_VALUE(param);
+
+                       if (!TREE_CHAIN(param) && TREE_CODE(param_type) == VOID_TYPE)
+                               break;
+
+                       struct node *decl = stack_pull(&parameters);
+                       if (decl) {
+                               fprintf(stderr, "(pull parameter '%s')\n", decl->name);
+                               stack_push(&args, decl);
+                       } else
+                               fprintf(stderr, "ERROR: parameter %d missing parameter declaration\n", id);
+                       id++;
+               }
+               name = args.head;
+       }
 
-       for (tree param = first_param; param; param = TREE_CHAIN(param)) {
+       for (tree param = TYPE_ARG_TYPES(func); param; param = TREE_CHAIN(param)) {
                tree param_type = TREE_VALUE(param);
                const size_t data_size = value_size(TYPE_SIZE(param_type));
+               const char *names = NULL;
 
                // non-varags functions end in VOID_TYPE
                if (!TREE_CHAIN(param) && TREE_CODE(param_type) == VOID_TYPE)
@@ -371,8 +536,18 @@ static void export_params(tree first_param, size_t indent_level) {
                // size: do we need it?
                fprintf(output_file, " size => %zu,", data_size);
 
-               // name: none available, use position
-               sprintf(nameb, "arg_%d", id);
+               if (name) {
+                       // this should be a parm_decl with an identifier of the name
+                       names = name->name;
+
+                       // can one check it's a matching type?
+                       name = name->next;
+               }
+
+               if (!names || !names[0]) {
+                       sprintf(nameb, "arg_%d", id);
+                       names = nameb;
+               }
 
                fprintf(output_file, " name => '%s',", names);
                stack_push(&context_stack, node_alloc(param, names));
@@ -385,6 +560,8 @@ static void export_params(tree first_param, size_t indent_level) {
                fprintf(output_file, "},\n");
                id++;
        }
+
+       list_clear(&args);
 }
 
 /*
@@ -405,7 +582,8 @@ static void export_fields(tree first_field, size_t base_offset, int indent) {
                } else {
                        const char *names = IDENTIFIER_POINTER(DECL_NAME(field));
 
-                       printf("   name=%s\n", names);
+                       if (debug_level > 1)
+                               fprintf(stderr, "   field: %s\n", names);
                        print_spaces(indent+1);
                        fprintf(output_file, "{ name => '%s', size => %zu, offset => %zu,", names, field_size, offset);
                        stack_push(&context_stack, node_alloc(field, names));
@@ -428,7 +606,8 @@ static void export_type(tree type, const char *names) {
        tree deftype;
        tree target;
 
-       D(printf("export: %s %s\n", names, ZTREE_CODE(type)));
+       if (debug_level > 1)
+               fprintf(stderr, "export_type(%s, %s)\n", ZTREE_CODE(type), names);
 
        switch (TREE_CODE(type)) {
        case TYPE_DECL: {
@@ -442,13 +621,16 @@ static void export_type(tree type, const char *names) {
                        return;
                hash_put(&dumped, node_alloc(type, names));
 
+               if (debug_level > 1)
+                       fprintf(stderr, "export: %s %s\n", names, ZTREE_CODE(type));
+
                deftype = TREE_TYPE(type);
                target = target_type(deftype);
                switch (TREE_CODE(target)) {
                case FUNCTION_TYPE: {
+                       // function pointer typdef
                        // I don't know if i even want this
-
-                       fprintf(output_file, "'call:%s' => { name => '%s',", names, names);
+                       fprintf(output_file, "'call:%s' => { name => '%s', type => 'call',", names, names);
 
                        // the deftype is always a pointer for a function_type
 
@@ -459,6 +641,8 @@ static void export_type(tree type, const char *names) {
                        fprintf(output_file, " deref => '%s',", b.data);
                        free(b.data);
 
+                       fprintf(output_file, " ctype => '%s',", print_generic_expr_to_str(target));
+
                        // TODO: cleanup
                        {
                                tree result_type = TREE_TYPE(target);
@@ -470,16 +654,10 @@ static void export_type(tree type, const char *names) {
                        }
 
                        fprintf(output_file, "\n\targuments => [\n");
-                       export_params(TYPE_ARG_TYPES(target), 0);
+                       export_params(target);
                        fprintf(output_file, "]},\n");
                        break;
                }
-               case RECORD_TYPE:
-                       printf(" typedef record: %s\n", names);
-                       break;
-               case UNION_TYPE:
-                       printf(" typedef union: %s\n", names);
-                       break;
                case ENUMERAL_TYPE: {
                        // TODO: typedef of anonymous enumerations may be repeated
                        // TODO: this could be detected in the frontend - e.g. don't include any
@@ -503,12 +681,16 @@ static void export_type(tree type, const char *names) {
                        fprintf(output_file, "]},\n");
                        break;
                }
+               case RECORD_TYPE: // forward declaration or opaque types
+               case UNION_TYPE:
                case VOID_TYPE:
                case INTEGER_TYPE:
                case REAL_TYPE:
+                       if (debug_level)
+                               fprintf(stderr, "ignored %s: %s\n", ZTREE_CODE(target), names);
                        break;
                default:
-                       printf("unknown type def: %s\n", ZTREE_CODE(target));
+                       fprintf(stderr, "unknown type def: %s\n", ZTREE_CODE(target));
                        gcc_unreachable();
                }
 
@@ -524,13 +706,17 @@ static void export_type(tree type, const char *names) {
                if (hash_lookup(&dumped, names))
                        return;
                hash_put(&dumped, node_alloc(type, names));
-               printf("export type func decl %s\n", names);
+
+               if (debug_level > 1)
+                       fprintf(stderr, "export type func decl %s\n", names);
 
                fprintf(output_file, "'func:%s' => { name => '%s', type => 'func',", names, names);
 
                // FUNCTION_DECL -> FUNCTION_TYPE -> RESULT_TYPE, get FUNCTION_TYPE
                type = TREE_TYPE(type);
 
+               fprintf(output_file, " ctype => '%s',", print_generic_expr_to_str(type));
+
                // TODO: cleanup
                debug_tree_helper(type, "function 1");
                {
@@ -544,11 +730,13 @@ static void export_type(tree type, const char *names) {
 
                fprintf(output_file, "\n\targuments => [\n");
                //export_decl_params(DECL_ARGUMENTS(type), 0);
-               export_params(TYPE_ARG_TYPES(type), 0);
+               export_params(type);
                fprintf(output_file, "]},\n");
                break;
        }
        case FUNCTION_TYPE: {
+               // This is called for un-typedef'd function pointers.
+               // WHY IS THIS DIFFERENT TO ABOVE?
                if (!names) {
                        name = TYPE_IDENTIFIER(type);
                        if (!name)
@@ -558,9 +746,12 @@ static void export_type(tree type, const char *names) {
                if (hash_lookup(&dumped, names))
                        return;
                hash_put(&dumped, node_alloc(type, names));
-               printf("export type func %s\n", names);
+
+               if (debug_level > 1)
+                       fprintf(stderr, "export: %s %s\n", names, ZTREE_CODE(type));
 
                fprintf(output_file, "'call:%s' => { name => '%s', type => 'call',", names, names);
+               fprintf(output_file, " ctype => '%s',", print_generic_expr_to_str(type));
 
                debug_tree_helper(type, "function type");
 
@@ -572,7 +763,8 @@ static void export_type(tree type, const char *names) {
                        //printf(" result type type %s\n", ZTREE_CODE(result_type));
                        const size_t data_size = value_size(TYPE_SIZE(result));
 
-                       printf(" result size %zu\n", data_size);
+                       if (debug_level > 2)
+                               fprintf(stderr, " result size %zu\n", data_size);
                        fprintf(output_file, "\n\tresult => {");
                        export_param(type, result, data_size);
                        fprintf(output_file, " },");
@@ -580,7 +772,7 @@ static void export_type(tree type, const char *names) {
 
                stack_push(&context_stack, node_alloc(type, names));
                fprintf(output_file, "\n\targuments => [\n");
-               export_params(TYPE_ARG_TYPES(type), 0);
+               export_params(type);
                free(stack_pull(&context_stack));
                fprintf(output_file, "]},\n");
                break;
@@ -603,6 +795,9 @@ static void export_type(tree type, const char *names) {
                        return;
                hash_put(&dumped, node_alloc(type, names));
 
+               if (debug_level > 1)
+                       fprintf(stderr, "export: %s %s\n", names, ZTREE_CODE(type));
+
                fprintf(output_file, "'%s:%s' => { name => '%s', type => '%s', size => %zu, fields => [\n",
                        su, names, names, su, tree_to_uhwi(TYPE_SIZE(type)));
 
@@ -647,6 +842,10 @@ static void export_type(tree type, const char *names) {
                        sprintf(nameb, "enum$%d", namei++);
                        names = nameb;
                }
+
+               if (debug_level > 1)
+                       fprintf(stderr, "export: %s %s\n", names, ZTREE_CODE(type));
+
                fprintf(output_file, "'enum:%s' => { name => '%s', type => 'enum', size => %zu, value_type => '%c%zu', values => [\n",
                        names, names, size, TYPE_UNSIGNED(type) ? 'u' : 'i', size);
 
@@ -659,14 +858,56 @@ static void export_type(tree type, const char *names) {
                break;
        }
        case FIELD_DECL:
-       case PARM_DECL:
                break;
+       case PARM_DECL: {
+               // capture PARM_DECLs so they can be used at next function declaration
+               if (!names) {
+                       name = DECL_NAME(type);
+                       if (name)
+                               names = IDENTIFIER_POINTER(name);
+               }
+               // if this is a function pointer typedef, need to suck out the arguments at this point
+               deftype = TREE_TYPE(type);
+
+               target = target_type(deftype);
+
+               //fprintf(stderr, "type is '%s\n", ZTREE_CODE(deftype));
+               //fprintf(stderr, "target is '%s\n", ZTREE_CODE(target));
+
+               if (TREE_CODE(target) == FUNCTION_TYPE) {
+                       // We need to save the list of parameters for later,
+                       // it's keyed on target
+                       struct node *fwd = node_alloc(target, NULL);
+
+                       fprintf(stderr, "save forward reference function type %p\n", target);
+
+                       for (tree param = TYPE_ARG_TYPES(target); param != NULL; param = TREE_CHAIN(param)) {
+                               tree param_type = TREE_VALUE(param);
+
+                               if (TREE_CODE(param_type) == VOID_TYPE)
+                                       break;
+
+                               struct node *decl = stack_pull(&parameters);
+                               if (decl) {
+                                       fprintf(stderr, "(pull parameter '%s')\n", decl->name);
+                                       stack_push(&fwd->list, decl);
+                               } else
+                                       fprintf(stderr, " missing parameter name\n");
+                       }
+
+                       hash_put_bytype(&forward_types, fwd);
+               }
+
+               fprintf(stderr, "(push parameter '%s')\n", names);
+               stack_push(&parameters, node_alloc(type, names));
+
+               break; }
        case VAR_DECL:
                // global external variables, might want these
                // well, if there's a way to resolve them
                break;
        default:
-               printf("unknown export: %s\n", ZTREE_CODE(type));
+               fprintf(stderr, "unknown export: %s\n", ZTREE_CODE(type));
                gcc_unreachable();
        }
 }
@@ -674,16 +915,27 @@ static void export_type(tree type, const char *names) {
 static void plugin_finish_type(void *event_data, void *user_data) {
        tree type = (tree)event_data;
 
+       if (debug_level > 0)
+               fprintf(stderr, "finish_type\n");
+       if (debug_level > 1)
+               debug_tree(type);
+
        export_type(type, NULL);
 }
 
 static void plugin_finish_decl(void *event_data, void *user_data) {
        tree type = (tree)event_data;
 
+       if (debug_level > 0)
+               fprintf(stderr, "finish_decl %s\n", ZTREE_CODE(type));
+       if (debug_level > 1)
+               debug_tree(type);
+
        export_type(type, NULL);
 }
 
 static void plugin_finish(void *event_data, void *user_data) {
+       fprintf(stderr, "plugin finish\n");
        for (struct node *n = todump.head; n; n=n->next) {
                if (COMPLETE_TYPE_P(n->type)) {
                        if (n->name[0]) {
@@ -699,34 +951,39 @@ static void plugin_finish(void *event_data, void *user_data) {
                fprintf(output_file, "# %s\n", n->name);
 
        fprintf(output_file, ");\n");
-       //fclose(output_file);
+       fclose(output_file);
+
+       fprintf(stderr, "unhandled paramters:\n");
+       list_clear(&parameters);
 }
 
 int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version) {
-    const char *output = NULL;
+       const char *output = NULL;
 
-    for (int i = 0; i < plugin_info->argc; ++i) {
-        if (0 == strcmp(plugin_info->argv[i].key, "output")) {
-            output = plugin_info->argv[i].value;
-        }
-    }
+       for (int i = 0; i < plugin_info->argc; ++i) {
+               if (0 == strcmp(plugin_info->argv[i].key, "output")) {
+                       output = plugin_info->argv[i].value;
+               } else if (0 == strcmp(plugin_info->argv[i].key, "debug")) {
+                       debug_level = atoi(plugin_info->argv[i].value);
+               }
+       }
 
-    if (NULL == output) {
-        fprintf(stderr, "export plugin: missing parameter: -fplugin-arg-export-output=<output>\n");
-        exit(EXIT_FAILURE);
-    }
+       if (NULL == output) {
+               fprintf(stderr, "export plugin: missing parameter: -fplugin-arg-export-output=<output>\n");
+               exit(EXIT_FAILURE);
+       }
 
-    output_file = fopen(output, "w");
-    if (NULL == output_file) {
-        perror(output);
-        exit(EXIT_FAILURE);
-    }
+       output_file = fopen(output, "w");
+       if (NULL == output_file) {
+               perror(output);
+               exit(EXIT_FAILURE);
+       }
 
-    fprintf(output_file, "%%data = (\n");
+       fprintf(output_file, "%%data = (\n");
 
-    register_callback(plugin_info->base_name, PLUGIN_FINISH_DECL, plugin_finish_decl, NULL);
-    register_callback(plugin_info->base_name, PLUGIN_FINISH_TYPE, plugin_finish_type, NULL);
-    register_callback(plugin_info->base_name, PLUGIN_FINISH, plugin_finish, NULL);
+       register_callback(plugin_info->base_name, PLUGIN_FINISH_DECL, plugin_finish_decl, NULL);
+       register_callback(plugin_info->base_name, PLUGIN_FINISH_TYPE, plugin_finish_type, NULL);
+       register_callback(plugin_info->base_name, PLUGIN_FINISH, plugin_finish, NULL);
 
-    return 0;
+       return 0;
 }