[comp.lang.c++] GDB slowness

bryan@kewill.UUCP (Bryan Boreham) (03/17/90)

Paul Vaughan (vaughan@mcc.com) writes:

> In using gdb, I often suffer long delays ...

Me too :-)

G++ traditionally outputs all the information that might conceivably
be useful to a debugger, in every .o file.  Since C++ programs tend to
#include a lot of the same headers in every source file, this leads to
duplication in the binary.  It hit me pretty hard, since the smallest
ET++ program would wind up at around 17Mb, and the biggest machine
here only had 8Mb of RAM...

I suggested to Michael that this was wrong, and he implemented a flag
-fminimal-debug.  This has been in g++ since version 1.36, and it only
outputs debug info for a class if code is generated in this
compilation.  For systems structured as one .cc file per class, this
can be a big win.  My ET++ binaries come down to around 5Mb, still
with all the information gdb needs.

However, gdb has to be patched slightly to encourage it to look for
this information.  This feature should be present in version 4.0 when
it is released, but for those who can't wait, diffs follow (against
gdb 3.4 or so -- 3.5 is very much the same).

If you find a bug with this stuff and fix it, let me know.  If you
find a bug and can't fix it, wait for gdb version 4.0.  C++
development at Kewill Systems is currently halted, but we used
-fminimal-debug for three months, so it should be mostly OK.

I'm not sure that this will solve Paul's problems, but it it's bound
to be of interest to somebody.

Bryan Boreham			bryan@kewill.uucp  
Software Engineer	||	bryan%kewill@uunet.uu.net
Kewill Systems PLC	||  ... uunet!mcvax!ukc!root44!kewill!bryan
Walton-On-Thames	
Surrey, England		Telephone: (+44) 932 248 328




*** dbxread.c.orig	Thu Nov  2 01:09:44 1989
--- dbxread.c	Mon Nov 13 20:14:24 1989
***************
*** 4084,4090 ****
  }
  
  /* Add here something to go through each undefined type, see if it's
!    still undefined, and do a full lookup if so.  */
  static void
  cleanup_undefined_types ()
  {
--- 4084,4103 ----
  }
  
  /* Add here something to go through each undefined type, see if it's
! still undefined, and do a full lookup if so.  */ 
! 
! /* 
!    There is now a routine "check_stub_type" to check for undefined types
!    at the time they are printed, and see if they can be resolved in some
!    other file. However, this routine is still here so that references
!    within a file are resolved to the most local definition.  I.e., if
!    there are two different "struct foo"s in a program, then this routine
!    will fix up all references in files that contain a definition.
! 
!    Probably, all undefined types should be resolved at the time they
!    are output, and this routine should be removed.
! */
! 
  static void
  cleanup_undefined_types ()
  {
***************
*** 4240,4245 ****
--- 4253,4269 ----
  	     But sometimes (ie. when the cross ref is the last thing on
  	     the line) there will be no ','.  */
  	  from = (char *) index (*pp, ',');
+ 
+ /* Change by Bryan Boreham, Kewill, Tue Oct 31 14:53:00 1989.
+    It can be that there is a comma, but it is part of the next section
+    of the stab; i.e. after the next semi-colon. If this is the case,
+    pretend there is no comma.    */
+ 	  {
+ 	    char *semi = (char *) index (*pp, ';');
+ 	    if (semi != 0 && from > semi)
+ 	      from = 0;
+ 	  }
+ 
  	  if (from)
  	    *pp = from;
  	}
*** symseg.h.orig	Wed Sep 27 20:54:30 1989
--- symseg.h	Mon Nov 13 20:14:26 1989
***************
*** 134,139 ****
--- 134,147 ----
     someone referenced a type that wasn't definined in a source file
     via (struct sir_not_appearing_in_this_film *)).  */
  #define TYPE_FLAG_STUB 8
+ 
+ /* Change by Bryan Boreham, Kewill, Sun Sep 17 18:04:48 1989.
+    This appears in a type's flags word if it is an undefined type and
+    we have already checked to see if it can be resolved in some other 
+    file. It breaks some uses of add-file command, and should be
+    removed. */
+ #define TYPE_FLAG_STUB_CHECKED                16
+ 
  /* Set when a class has a constructor defined */
  #define	TYPE_FLAG_HAS_CONSTRUCTOR	256
  /* Set when a class has a destructor defined */
*** symtab.c.orig	Sun Nov  5 19:08:22 1989
--- symtab.c	Mon Nov 13 20:14:29 1989
***************
*** 175,180 ****
--- 175,218 ----
    return 0;
  }
  
+ /* Added by Bryan Boreham, Kewill, Sun Sep 17 18:07:17 1989.
+ 
+    If this is a stubbed struct (i.e. declared as struct foo *), see if
+    we can find a full definition in some other file. If so, copy this
+    definition, so we can use it in future.  If not, set a flag so we 
+    don't waste too much time in future.
+ 
+    This used to be coded as a macro, but I don't think it is called 
+    often enough to merit such treatment.
+ */
+ 
+ void 
+ check_stub_type(type)
+      struct type *type;
+ {
+   if (TYPE_FLAGS(type) & TYPE_FLAG_STUB & ~TYPE_FLAG_STUB_CHECKED)
+     {
+       char* name= TYPE_NAME (type);
+       struct symbol *sym;
+       switch (TYPE_CODE(type))
+ 	{
+ 	case TYPE_CODE_STRUCT:
+ 	  name += 7;		/* skip past "struct " */
+ 	  break;
+ 	case TYPE_CODE_UNION:
+ 	  name += 6;		/* skip past "union " */
+ 	  break;
+ 	case TYPE_CODE_ENUM:
+ 	  name += 5;		/* skip past "enum " */
+ 	  break;
+ 	}
+       if (sym = lookup_symbol (name, 0, STRUCT_NAMESPACE, 0))
+ 	bcopy (SYMBOL_TYPE(sym), type, sizeof (struct type));
+       else
+ 	TYPE_FLAGS(type) |= TYPE_FLAG_STUB_CHECKED;
+     }
+ }
+ 
  /* Lookup a typedef or primitive type named NAME,
     visible in lexical block BLOCK.
     If NOERR is nonzero, return zero if NAME is not suitably defined.  */
***************
*** 540,545 ****
--- 578,596 ----
        main_type = btype;
        bzero (btype, sizeof (struct type));
        TYPE_MAIN_VARIANT (btype) = main_type;
+ 
+ /* Change by Bryan Boreham, Kewill, Thu Sep 21 14:04:47 1989.
+    I don't know what is the idea behind returning a mostly-zeroed 
+    type whose main variant is itself, but it fouls up completely 
+    when I try to use g++ -fminimal-debug.
+ 
+    So, try to detect this; save the name and hope to fix it up later.   */
+       if (TYPE_FLAGS(type) & TYPE_FLAG_STUB)
+ 	{
+ 	  TYPE_NAME(btype) = TYPE_NAME(type);
+ 	  TYPE_FLAGS(btype) |= TYPE_FLAG_STUB;
+ 	}
+ 
      }
    else
      {
***************
*** 1576,1581 ****
--- 1627,1633 ----
  			class_name += 6;
  
  		      sym_class = lookup_symbol (class_name, 0, STRUCT_NAMESPACE, 0);
+ 		      if (sym_class)  /* may not find class if t is a stub -- bryan */
  		      for (method_counter = TYPE_NFN_FIELDS (SYMBOL_TYPE (sym_class)) - 1;
  			   method_counter >= 0;
  			   --method_counter)
*** symtab.h.orig	Fri Sep  8 01:59:14 1989
--- symtab.h	Mon Nov 13 20:19:14 1989
***************
*** 284,289 ****
--- 284,291 ----
  extern struct symtab *lookup_symtab ();
  extern struct symbol *lookup_symbol ();
  extern struct type *lookup_typename ();
+ extern void check_stub_type ();
+ extern struct type *lookup_primitive_typename ();
  extern struct type *lookup_unsigned_typename ();
  extern struct type *lookup_struct ();
  extern struct type *lookup_union ();
*** valops.c.orig	Sun Nov  5 18:04:33 1989
--- valops.c	Thu Nov 23 21:16:29 1989
***************
*** 826,831 ****
--- 826,833 ----
  	 is less work to be done.  */
        while (t)
  	{
+ 	  check_stub_type (t);
+ 
  	  for (i = TYPE_NFIELDS (t) - 1; i >= 0; i--)
  	    {
  	      char *t_field_name = TYPE_FIELD_NAME (t, i);
***************
*** 893,898 ****
--- 895,902 ----
    /*   This following loop is for methods with arguments.  */
    while (t)
      {
+       check_stub_type (t);
+ 
        /* Look up as method first, because that is where we
  	 expect to find it first.  */
        for (i = TYPE_NFN_FIELDS (t) - 1; i >= 0; i--)
***************
*** 936,941 ****
--- 940,949 ----
        t = baseclass;
        while (t)
  	{
+ 
+ 	  /* minimal-debug: we don't need to do a check_stub_type here,
+ 	     we will have been through all the baseclasses already. */
+ 
  	  for (i = TYPE_NFIELDS (t) - 1; i >= 0; i--)
  	    {
  	      char *t_field_name = TYPE_FIELD_NAME (t, i);
*** valprint.c.orig	Fri Sep 29 04:32:13 1989
--- valprint.c	Mon Nov 13 20:14:31 1989
***************
*** 272,277 ****
--- 272,279 ----
    
    QUIT;
  
+   check_stub_type(type);
+ 
    if (TYPE_FLAGS (type) & TYPE_FLAG_STUB)
      {
        fprintf_filtered (stream, "<Type not defined in this context>");
***************
*** 869,874 ****
--- 871,879 ----
  
    while (type && n_baseclasses == 1)
      {
+ 
+       check_stub_type(type);	/* Not actually sure about this one -- Bryan. */
+ 
        basetype = TYPE_BASECLASS (type, 1);
        if (TYPE_NAME (basetype) && (name = TYPE_NAME (basetype)))
  	{
***************
*** 1127,1132 ****
--- 1132,1139 ----
        else
  	{
  	  int i;
+ 
+ 	  check_stub_type (type);
  
  	  type_print_derivation_info (stream, type);
  	  
*** values.c.orig	Sun Sep 10 04:39:19 1989
--- values.c	Thu Nov 23 21:47:24 1989
***************
*** 58,63 ****
--- 58,65 ----
  {
    register value val;
  
+   check_stub_type (type);
+ 
    val = (value) xmalloc (sizeof (struct value) + TYPE_LENGTH (type));
    VALUE_NEXT (val) = all_values;
    all_values = val;
***************
*** 84,89 ****
--- 86,93 ----
  {
    register value val;
  
+   check_stub_type (type);
+ 
    val = (value) xmalloc (sizeof (struct value) + TYPE_LENGTH (type) * count);
    VALUE_NEXT (val) = all_values;
    all_values = val;
***************
*** 650,657 ****
       register int fieldno;
  {
    register value v;
!   register struct type *type = TYPE_FIELD_TYPE (VALUE_TYPE (arg1), fieldno);
    register int offset;
  
    /* Handle packed fields */
  
--- 654,664 ----
       register int fieldno;
  {
    register value v;
!   register struct type *type;
    register int offset;
+ 
+   check_stub_type (VALUE_TYPE (arg1));
+   type = TYPE_FIELD_TYPE (VALUE_TYPE (arg1), fieldno);
  
    /* Handle packed fields */
  

rfg@ics.uci.edu (Ronald Guilmette) (03/21/90)

In article <9003161607.AA06581@kewill.uucp> bryan@kewill.UUCP (Bryan Boreham) writes:
>Paul Vaughan (vaughan@mcc.com) writes:
>
>> In using gdb, I often suffer long delays ...
>
>Me too :-)
>
>G++ traditionally outputs all the information that might conceivably
>be useful to a debugger, in every .o file.  Since C++ programs tend to
>#include a lot of the same headers in every source file, this leads to
>duplication in the binary.  It hit me pretty hard, since the smallest
>ET++ program would wind up at around 17Mb, and the biggest machine
>here only had 8Mb of RAM...
>
>I suggested to Michael that this was wrong, and he implemented a flag
>-fminimal-debug.  This has been in g++ since version 1.36, and it only
>outputs debug info for a class if code is generated in this
>compilation.  For systems structured as one .cc file per class, this
>can be a big win.  My ET++ binaries come down to around 5Mb, still
>with all the information gdb needs.

So this reduces the duplicated debugging info for classes.  Great.  Now
what about typedefs?  What about enum types?  What about good old fashioned
structs and unions?  What about extern declarations for data objects and
functions?  This stuff can all be present in include files also and it
can also lead to duplication of debug information in executable files.

Frankly, the solution described above sounds like a kludge.  A better
solution would be to have a "smart linker" which eliminated the duplicate
information for *all* categories of things while it was doing linking.
This could be done in GNU ld and in COFF and ELF linkers, and it would
provide even greater savings in disk space, and even faster debugging.

// Ron Guilmette (rfg@ics.uci.edu)
// C++ Entomologist
// Motto:  If it sticks, force it.  If it breaks, it needed replacing anyway.