OSDN Git Service

2009-04-16 Rafael Avila de Espindola <espindola@google.com>
[pf3gnuchains/gcc-fork.git] / gcc / plugin.c
1 /* Support for GCC plugin mechanism.
2    Copyright (C) 2009 Free Software Foundation, Inc.
3
4 This file is part of GCC.
5
6 GCC 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, or (at your option)
9 any later version.
10
11 GCC 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.
15
16 You should have received a copy of the GNU General Public License
17 along with GCC; see the file COPYING3.  If not see
18 <http://www.gnu.org/licenses/>.  */
19
20 /* This file contains the support for GCC plugin mechanism based on the
21    APIs described in doc/plugin.texi.  */
22
23 #include "config.h"
24 #include "system.h"
25
26 /* If plugin support is not enabled, do not try to execute any code
27    that may reference libdl.  The generic code is still compiled in to
28    avoid including to many conditional compilation paths in the rest
29    of the compiler.  */
30 #ifdef ENABLE_PLUGIN
31 #include <dlfcn.h>
32 #endif
33
34 #include "coretypes.h"
35 #include "toplev.h"
36 #include "tree.h"
37 #include "tree-pass.h"
38 #include "intl.h"
39 #include "plugin.h"
40 #include "timevar.h"
41
42 /* Event names as strings.  Keep in sync with enum plugin_event.  */
43 const char *plugin_event_name[] =
44 {
45   "PLUGIN_PASS_MANAGER_SETUP",
46   "PLUGIN_FINISH_TYPE",
47   "PLUGIN_FINISH_UNIT",
48   "PLUGIN_CXX_CP_PRE_GENERICIZE",
49   "PLUGIN_FINISH",
50   "PLUGIN_INFO",
51   "PLUGIN_EVENT_LAST"
52 };
53
54 /* Object that keeps track of the plugin name and its arguments
55    when parsing the command-line options -fplugin=/path/to/NAME.so and
56    -fplugin-arg-NAME-<key>[=<value>].  */
57 struct plugin_name_args
58 {
59   char *base_name;
60   const char *full_name;
61   int argc;
62   struct plugin_argument *argv;
63   const char *version;
64 };
65
66 /* Hash table for the plugin_name_args objects created during command-line
67    parsing.  */
68 static htab_t plugin_name_args_tab = NULL;
69
70 /* List node for keeping track of plugin-registered callback.  */
71 struct callback_info
72 {
73   const char *plugin_name;   /* Name of plugin that registers the callback.  */
74   plugin_callback_func func; /* Callback to be called.  */
75   void *user_data;           /* plugin-specified data.  */
76   struct callback_info *next;
77 };
78
79 /* An array of lists of 'callback_info' objects indexed by the event id.  */
80 static struct callback_info *plugin_callbacks[PLUGIN_EVENT_LAST] = { NULL };
81
82 /* List node for an inserted pass instance. We need to keep track of all
83    the newly-added pass instances (with 'added_pass_nodes' defined below)
84    so that we can register their dump files after pass-positioning is finished.
85    Registering dumping files needs to be post-processed or the
86    static_pass_number of the opt_pass object would be modified and mess up
87    the dump file names of future pass instances to be added.  */
88 struct pass_list_node
89 {
90   struct opt_pass *pass;
91   struct pass_list_node *next;
92 };
93
94 static struct pass_list_node *added_pass_nodes = NULL;
95 static struct pass_list_node *prev_added_pass_node;
96
97 #ifdef ENABLE_PLUGIN
98 /* Each plugin should define an initialization function with exactly
99    this name.  */
100 static const char *str_plugin_init_func_name = "plugin_init";
101 #endif
102
103 /* Helper function for the hash table that compares the base_name of the
104    existing entry (S1) with the given string (S2).  */
105
106 static int
107 htab_str_eq (const void *s1, const void *s2)
108 {
109   const struct plugin_name_args *plugin = (const struct plugin_name_args *) s1;
110   return !strcmp (plugin->base_name, (const char *) s2);
111 }
112
113
114 /* Given a plugin's full-path name FULL_NAME, e.g. /pass/to/NAME.so,
115    return NAME.  */
116
117 static char *
118 get_plugin_base_name (const char *full_name)
119 {
120   /* First get the base name part of the full-path name, i.e. NAME.so.  */
121   char *base_name = xstrdup (lbasename (full_name));
122
123   /* Then get rid of '.so' part of the name.  */
124   strip_off_ending (base_name, strlen (base_name));
125
126   return base_name;
127 }
128
129
130 /* Create a plugin_name_args object for the give plugin and insert it to
131    the hash table. This function is called when -fplugin=/path/to/NAME.so
132    option is processed.  */
133
134 void
135 add_new_plugin (const char* plugin_name)
136 {
137   struct plugin_name_args *plugin;
138   void **slot;
139   char *base_name = get_plugin_base_name (plugin_name);
140
141   /* If this is the first -fplugin= option we encounter, create 
142      'plugin_name_args_tab' hash table.  */
143   if (!plugin_name_args_tab)
144     plugin_name_args_tab = htab_create (10, htab_hash_string, htab_str_eq,
145                                         NULL);
146
147   slot = htab_find_slot (plugin_name_args_tab, base_name, INSERT);
148
149   /* If the same plugin (name) has been specified earlier, either emit an
150      error or a warning message depending on if they have identical full
151      (path) names.  */
152   if (*slot)
153     {
154       plugin = (struct plugin_name_args *) *slot;
155       if (strcmp (plugin->full_name, plugin_name))
156         error ("Plugin %s was specified with different paths:\n%s\n%s",
157                plugin->base_name, plugin->full_name, plugin_name);
158       return;
159     }
160
161   plugin = XCNEW (struct plugin_name_args);
162   plugin->base_name = base_name;
163   plugin->full_name = plugin_name;
164
165   *slot = plugin;
166 }
167
168
169 /* Parse the -fplugin-arg-<name>-<key>[=<value>] option and create a
170    'plugin_argument' object for the parsed key-value pair. ARG is
171    the <name>-<key>[=<value>] part of the option.  */
172
173 void
174 parse_plugin_arg_opt (const char *arg)
175 {
176   size_t len = 0, name_len = 0, key_len = 0, value_len = 0;
177   const char *ptr, *name_start = arg, *key_start = NULL, *value_start = NULL;
178   char *name, *key, *value;
179   void **slot;
180   bool name_parsed = false, key_parsed = false;
181
182   /* Iterate over the ARG string and identify the starting character position
183      of 'name', 'key', and 'value' and their lengths.  */
184   for (ptr = arg; *ptr; ++ptr)
185     {
186       /* Only the first '-' encountered is considered a separator between
187          'name' and 'key'. All the subsequent '-'s are considered part of
188          'key'. For example, given -fplugin-arg-foo-bar-primary-key=value,
189          the plugin name is 'foo' and the key is 'bar-primary-key'.  */
190       if (*ptr == '-' && !name_parsed)
191         {
192           name_len = len;
193           len = 0;
194           key_start = ptr + 1;
195           name_parsed = true;
196           continue;
197         }
198       else if (*ptr == '=')
199         {
200           if (key_parsed)
201             {
202               error ("Malformed option -fplugin-arg-%s (multiple '=' signs)",
203                      arg);
204               return;
205             }
206           key_len = len;
207           len = 0;
208           value_start = ptr + 1;
209           key_parsed = true;
210           continue;
211         }
212       else
213         ++len;
214     }
215
216   if (!key_start)
217     {
218       error ("Malformed option -fplugin-arg-%s (missing -<key>[=<value>])",
219              arg);
220       return;
221     }
222
223   /* If the option doesn't contain the 'value' part, LEN is the KEY_LEN.
224      Otherwise, it is the VALUE_LEN.  */
225   if (!value_start)
226     key_len = len;
227   else
228     value_len = len;
229
230   name = XNEWVEC (char, name_len + 1);
231   strncpy (name, name_start, name_len);
232   name[name_len] = '\0';
233
234   /* Check if the named plugin has already been specified earlier in the
235      command-line.  */
236   if (plugin_name_args_tab
237       && ((slot = htab_find_slot (plugin_name_args_tab, name, NO_INSERT))
238           != NULL))
239     {
240       struct plugin_name_args *plugin = (struct plugin_name_args *) *slot;
241
242       key = XNEWVEC (char, key_len + 1);
243       strncpy (key, key_start, key_len);
244       key[key_len] = '\0';
245       if (value_start)
246         {
247           value = XNEWVEC (char, value_len + 1);
248           strncpy (value, value_start, value_len);
249           value[value_len] = '\0';
250         }
251       else
252         value = NULL;
253
254       /* Create a plugin_argument object for the parsed key-value pair.
255          If there are already arguments for this plugin, we will need to
256          adjust the argument array size by creating a new array and deleting
257          the old one. If the performance ever becomes an issue, we can
258          change the code by pre-allocating a larger array first.  */
259       if (plugin->argc > 0)
260         {
261           struct plugin_argument *args = XNEWVEC (struct plugin_argument,
262                                                   plugin->argc + 1);
263           memcpy (args, plugin->argv,
264                   sizeof (struct plugin_argument) * plugin->argc);
265           XDELETEVEC (plugin->argv);
266           plugin->argv = args;
267           ++plugin->argc;
268         }
269       else
270         {
271           gcc_assert (plugin->argv == NULL);
272           plugin->argv = XNEWVEC (struct plugin_argument, 1);
273           plugin->argc = 1;
274         }
275
276       plugin->argv[plugin->argc - 1].key = key;
277       plugin->argv[plugin->argc - 1].value = value;
278     }
279   else
280     error ("Plugin %s should be specified before -fplugin-arg-%s "
281            "in the command line", name, arg);
282
283   /* We don't need the plugin's name anymore. Just release it.  */
284   XDELETEVEC (name);
285 }
286
287
288 /* Insert the plugin pass at the proper position. Return true if the pass 
289    is successfully added.
290
291    PLUGIN_PASS_INFO - new pass to be inserted
292    PASS_LIST        - root of the pass list to insert the new pass to  */
293
294 static bool
295 position_pass (struct plugin_pass *plugin_pass_info,
296                struct opt_pass **pass_list)
297 {
298   struct opt_pass *pass = *pass_list, *prev_pass = NULL;
299   bool success = false;
300
301   for ( ; pass; prev_pass = pass, pass = pass->next)
302     {
303       /* Check if the current pass is of the same type as the new pass and
304          matches the name and the instance number of the reference pass.  */
305       if (pass->type == plugin_pass_info->pass->type
306           && pass->name
307           && !strcmp (pass->name, plugin_pass_info->reference_pass_name)
308           && ((plugin_pass_info->ref_pass_instance_number == 0)
309               || (plugin_pass_info->ref_pass_instance_number ==
310                   pass->static_pass_number)
311               || (plugin_pass_info->ref_pass_instance_number == 1
312                   && pass->todo_flags_start & TODO_mark_first_instance)))
313         {
314           struct opt_pass *new_pass = plugin_pass_info->pass;
315           struct pass_list_node *new_pass_node;
316
317           /* The following code (if-statement) is adopted from next_pass_1.  */
318           if (new_pass->static_pass_number)
319             {
320               new_pass = XNEW (struct opt_pass);
321               memcpy (new_pass, plugin_pass_info->pass, sizeof (*new_pass));
322               new_pass->next = NULL;
323
324               new_pass->todo_flags_start &= ~TODO_mark_first_instance;
325
326               plugin_pass_info->pass->static_pass_number -= 1;
327               new_pass->static_pass_number =
328                   -plugin_pass_info->pass->static_pass_number;
329             }
330           else
331             {
332               new_pass->todo_flags_start |= TODO_mark_first_instance;
333               new_pass->static_pass_number = -1;
334             }
335
336           /* Insert the new pass instance based on the positioning op.  */
337           switch (plugin_pass_info->pos_op)
338             {
339               case PASS_POS_INSERT_AFTER:
340                 new_pass->next = pass->next;
341                 pass->next = new_pass;
342                 break;
343               case PASS_POS_INSERT_BEFORE:
344                 new_pass->next = pass;
345                 if (prev_pass)
346                   prev_pass->next = new_pass;
347                 else
348                   *pass_list = new_pass;
349                 break;
350               case PASS_POS_REPLACE:
351                 new_pass->next = pass->next;
352                 if (prev_pass)
353                   prev_pass->next = new_pass;
354                 else
355                   *pass_list = new_pass;
356                 new_pass->sub = pass->sub;
357                 new_pass->tv_id = pass->tv_id;
358                 pass = new_pass;
359                 break;
360               default:
361                 error ("Invalid pass positioning operation");
362                 return false;
363             }
364
365           /* Save the newly added pass (instance) in the added_pass_nodes
366              list so that we can register its dump file later. Note that
367              we cannot register the dump file now because doing so will modify
368              the static_pass_number of the opt_pass object and therefore
369              mess up the dump file name of future instances.  */
370           new_pass_node = XCNEW (struct pass_list_node);
371           new_pass_node->pass = new_pass;
372           if (!added_pass_nodes)
373             added_pass_nodes = new_pass_node;
374           else
375             prev_added_pass_node->next = new_pass_node;
376           prev_added_pass_node = new_pass_node;
377
378           success = true;
379         }
380
381       if (pass->sub && position_pass (plugin_pass_info, &pass->sub))
382         success = true;
383     }
384
385   return success;
386 }
387
388
389 /* Hook into the pass lists (trees) a new pass registered by a plugin.
390
391    PLUGIN_NAME - display name for the plugin
392    PASS_INFO   - plugin pass information that specifies the opt_pass object,
393                  reference pass, instance number, and how to position
394                  the pass  */
395
396 static void
397 register_pass (const char *plugin_name, struct plugin_pass *pass_info)
398 {
399   if (!pass_info->pass)
400     {
401       error ("No pass specified when registering a new pass in plugin %s",
402              plugin_name);
403       return;
404     }
405
406   if (!pass_info->reference_pass_name)
407     {
408       error ("No reference pass specified for positioning the pass "
409              " from plugin %s", plugin_name);
410       return;
411     }
412
413   /* Try to insert the new pass to the pass lists. We need to check all
414      three lists as the reference pass could be in one (or all) of them.  */
415   if (!position_pass (pass_info, &all_lowering_passes)
416       && !position_pass (pass_info, &all_ipa_passes)
417       && !position_pass (pass_info, &all_passes))
418     error ("Failed to position pass %s registered by plugin %s. "
419            "Cannot find the (specified instance of) reference pass %s",
420            pass_info->pass->name, plugin_name, pass_info->reference_pass_name);
421   else
422     {
423       /* OK, we have successfully inserted the new pass. We need to register
424          the dump files for the newly added pass and its duplicates (if any).
425          Because the registration of plugin passes happens after the
426          command-line options are parsed, the options that specify single
427          pass dumping (e.g. -fdump-tree-PASSNAME) cannot be used for new
428          plugin passes. Therefore we currently can only enable dumping of
429          new plugin passes when the 'dump-all' flags (e.g. -fdump-tree-all)
430          are specified. While doing so, we also delete the pass_list_node
431          objects created during pass positioning.  */
432       while (added_pass_nodes)
433         {
434           struct pass_list_node *next_node = added_pass_nodes->next;
435           enum tree_dump_index tdi;
436           register_one_dump_file (added_pass_nodes->pass);
437           if (added_pass_nodes->pass->type == SIMPLE_IPA_PASS
438               || added_pass_nodes->pass->type == IPA_PASS)
439             tdi = TDI_ipa_all;
440           else if (added_pass_nodes->pass->type == GIMPLE_PASS)
441             tdi = TDI_tree_all;
442           else
443             tdi = TDI_rtl_all;
444           /* Check if dump-all flag is specified.  */
445           if (get_dump_file_info (tdi)->state)
446             get_dump_file_info (added_pass_nodes->pass->static_pass_number)
447                 ->state = get_dump_file_info (tdi)->state;
448           XDELETE (added_pass_nodes);
449           added_pass_nodes = next_node;
450         }
451     }
452 }
453
454
455 /* Register additional plugin information. NAME is the name passed to
456    plugin_init. INFO is the information that should be registered. */
457
458 static void
459 register_plugin_info (const char* name, struct plugin_info *info)
460 {
461   void **slot = htab_find_slot (plugin_name_args_tab, name, NO_INSERT);
462   struct plugin_name_args *plugin = (struct plugin_name_args *) *slot;
463   plugin->version = info->version;
464 }
465
466 /* Called from the plugin's initialization code. Register a single callback.
467    This function can be called multiple times.
468
469    PLUGIN_NAME - display name for this plugin
470    EVENT       - which event the callback is for
471    CALLBACK    - the callback to be called at the event
472    USER_DATA   - plugin-provided data   */
473
474 void
475 register_callback (const char *plugin_name,
476                    enum plugin_event event,
477                    plugin_callback_func callback,
478                    void *user_data)
479 {
480   switch (event)
481     {
482       case PLUGIN_PASS_MANAGER_SETUP:
483         register_pass (plugin_name, (struct plugin_pass *) user_data);
484         break;
485       case PLUGIN_INFO:
486         register_plugin_info (plugin_name, (struct plugin_info *) user_data);
487         break;
488       case PLUGIN_FINISH_TYPE:
489       case PLUGIN_FINISH_UNIT:
490       case PLUGIN_CXX_CP_PRE_GENERICIZE:
491       case PLUGIN_FINISH:
492         {
493           struct callback_info *new_callback;
494           if (!callback)
495             {
496               error ("Plugin %s registered a null callback function "
497                      "for event %s", plugin_name, plugin_event_name[event]);
498               return;
499             }
500           new_callback = XNEW (struct callback_info);
501           new_callback->plugin_name = plugin_name;
502           new_callback->func = callback;
503           new_callback->user_data = user_data;
504           new_callback->next = plugin_callbacks[event];
505           plugin_callbacks[event] = new_callback;
506         }
507         break;
508       case PLUGIN_EVENT_LAST:
509       default:
510         error ("Unkown callback event registered by plugin %s",
511                plugin_name);
512     }
513 }
514
515
516 /* Called from inside GCC.  Invoke all plug-in callbacks registered with
517    the specified event.
518
519    EVENT    - the event identifier
520    GCC_DATA - event-specific data provided by the compiler  */
521
522 void
523 invoke_plugin_callbacks (enum plugin_event event, void *gcc_data)
524 {
525   timevar_push (TV_PLUGIN_RUN);
526
527   switch (event)
528     {
529       case PLUGIN_FINISH_TYPE:
530       case PLUGIN_FINISH_UNIT:
531       case PLUGIN_CXX_CP_PRE_GENERICIZE:
532       case PLUGIN_FINISH:
533         {
534           /* Iterate over every callback registered with this event and
535              call it.  */
536           struct callback_info *callback = plugin_callbacks[event];
537           for ( ; callback; callback = callback->next)
538             (*callback->func) (gcc_data, callback->user_data);
539         }
540         break;
541
542       case PLUGIN_PASS_MANAGER_SETUP:
543       case PLUGIN_EVENT_LAST:
544       default:
545         gcc_assert (false);
546     }
547
548   timevar_pop (TV_PLUGIN_RUN);
549 }
550
551 #ifdef ENABLE_PLUGIN
552 /* We need a union to cast dlsym return value to a function pointer
553    as ISO C forbids assignment between function pointer and 'void *'.
554    Use explicit union instead of __extension__(<union_cast>) for
555    portability.  */
556 #define PTR_UNION_TYPE(TOTYPE) union { void *_q; TOTYPE _nq; }
557 #define PTR_UNION_AS_VOID_PTR(NAME) (NAME._q)
558 #define PTR_UNION_AS_CAST_PTR(NAME) (NAME._nq)
559
560 /* Try to initialize PLUGIN. Return true if successful. */
561
562 static bool
563 try_init_one_plugin (struct plugin_name_args *plugin)
564 {
565   void *dl_handle;
566   plugin_init_func plugin_init;
567   char *err;
568   PTR_UNION_TYPE (plugin_init_func) plugin_init_union;
569
570   dl_handle = dlopen (plugin->full_name, RTLD_NOW);
571   if (!dl_handle)
572     {
573       error ("Cannot load plugin %s\n%s", plugin->full_name, dlerror ());
574       return false;
575     }
576
577   /* Clear any existing error.  */
578   dlerror ();
579
580   PTR_UNION_AS_VOID_PTR (plugin_init_union) =
581       dlsym (dl_handle, str_plugin_init_func_name);
582   plugin_init = PTR_UNION_AS_CAST_PTR (plugin_init_union);
583
584   if ((err = dlerror ()) != NULL)
585     {
586       error ("Cannot find %s in plugin %s\n%s", str_plugin_init_func_name,
587              plugin->full_name, err);
588       return false;
589     }
590
591   /* Call the plugin-provided initialization routine with the arguments.  */
592   if ((*plugin_init) (plugin->base_name, plugin->argc, plugin->argv))
593     {
594       error ("Fail to initialize plugin %s", plugin->full_name);
595       return false;
596     }
597
598   return true;
599 }
600
601
602 /* Routine to dlopen and initialize one plugin. This function is passed to
603    (and called by) the hash table traverse routine. Return 1 for the
604    htab_traverse to continue scan, 0 to stop.
605
606    SLOT - slot of the hash table element
607    INFO - auxiliary pointer handed to hash table traverse routine
608           (unused in this function)  */
609
610 static int
611 init_one_plugin (void **slot, void * ARG_UNUSED (info))
612 {
613   struct plugin_name_args *plugin = (struct plugin_name_args *) *slot;
614   bool ok = try_init_one_plugin (plugin);
615   if (!ok)
616     {
617       htab_remove_elt (plugin_name_args_tab, plugin->base_name);
618       XDELETE (plugin);
619     }
620   return 1;
621 }
622
623 #endif  /* ENABLE_PLUGIN  */
624
625 /* Main plugin initialization function.  Called from compile_file() in
626    toplev.c.  */
627
628 void
629 initialize_plugins (void)
630 {
631   /* If no plugin was specified in the command-line, simply return.  */
632   if (!plugin_name_args_tab)
633     return;
634
635   timevar_push (TV_PLUGIN_INIT);
636  
637 #ifdef ENABLE_PLUGIN
638   /* Traverse and initialize each plugin specified in the command-line.  */
639   htab_traverse_noresize (plugin_name_args_tab, init_one_plugin, NULL);
640 #endif
641
642   timevar_pop (TV_PLUGIN_INIT);
643 }
644
645 /* Release memory used by one plugin. */
646
647 static int
648 finalize_one_plugin (void **slot, void * ARG_UNUSED (info))
649 {
650   struct plugin_name_args *plugin = (struct plugin_name_args *) *slot;
651   XDELETE (plugin);
652   return 1;
653 }
654
655 /* Free memory allocated by the plugin system. */
656
657 void
658 finalize_plugins (void)
659 {
660   if (!plugin_name_args_tab)
661     return;
662
663   /* We can now delete the plugin_name_args object as it will no longer
664      be used. Note that base_name and argv fields (both of which were also
665      dynamically allocated) are not freed as they could still be used by
666      the plugin code.  */
667
668   htab_traverse_noresize (plugin_name_args_tab, finalize_one_plugin, NULL);
669
670   /* PLUGIN_NAME_ARGS_TAB is no longer needed, just delete it.  */
671   htab_delete (plugin_name_args_tab);
672   plugin_name_args_tab = NULL;
673 }
674
675 /* Used to pass options to htab_traverse callbacks. */
676
677 struct print_options
678 {
679   FILE *file;
680   const char *indent;
681 };
682
683 /* Print the version of one plugin. */
684
685 static int
686 print_version_one_plugin (void **slot, void *data)
687 {
688   struct print_options *opt = (struct print_options *) data;
689   struct plugin_name_args *plugin = (struct plugin_name_args *) *slot;
690   const char *version = plugin->version ? plugin->version : "Unknown version.";
691
692   fprintf (opt->file, " %s%s: %s\n", opt->indent, plugin->base_name, version);
693   return 1;
694 }
695
696 /* Print the version of each plugin. */
697
698 void
699 print_plugins_versions (FILE *file, const char *indent)
700 {
701   struct print_options opt;
702   opt.file = file;
703   opt.indent = indent;
704   if (!plugin_name_args_tab || htab_elements (plugin_name_args_tab) == 0)
705     return;
706
707   fprintf (file, "%sVersions of loaded plugins:\n", indent);
708   htab_traverse_noresize (plugin_name_args_tab, print_version_one_plugin, &opt);
709 }
710
711
712 /* Return true if plugins have been loaded.  */
713
714 bool
715 plugins_active_p (void)
716 {
717   enum plugin_event event;
718
719   for (event = PLUGIN_PASS_MANAGER_SETUP; event < PLUGIN_EVENT_LAST; event++)
720     if (plugin_callbacks[event])
721       return true;
722
723   return false;
724 }
725
726
727 /* Dump to FILE the names and associated events for all the active
728    plugins.  */
729
730 void
731 dump_active_plugins (FILE *file)
732 {
733   enum plugin_event event;
734
735   if (!plugins_active_p ())
736     return;
737
738   fprintf (stderr, "Event\t\t\tPlugins\n");
739   for (event = PLUGIN_PASS_MANAGER_SETUP; event < PLUGIN_EVENT_LAST; event++)
740     if (plugin_callbacks[event])
741       {
742         struct callback_info *ci;
743
744         fprintf (file, "%s\t", plugin_event_name[event]);
745
746         for (ci = plugin_callbacks[event]; ci; ci = ci->next)
747           fprintf (file, "%s ", ci->plugin_name);
748
749         fprintf (file, "\n");
750       }
751 }
752
753
754 /* Dump active plugins to stderr.  */
755
756 void
757 debug_active_plugins (void)
758 {
759   dump_active_plugins (stderr);
760 }