--- procps-2.0.7/proc/readproc.h.threadbadhack	Mon Jul 10 18:53:37 2000
+++ procps-2.0.7/proc/readproc.h	Thu Aug  8 12:26:44 2002
@@ -119,6 +119,9 @@
         pcpu;           /* %CPU usage (is not filled in by readproc!!!) */
     char
     	state;		/* single-char code for process state (S=sleeping) */
+    char
+        is_subthread;   /* If this is a subthread of a thread group
+			   (is not filled in by readproc!!!) */
 } proc_t;
 
 /* PROCTAB: data structure holding the persistent information readproc needs
--- procps-2.0.7/ps/common.h.threadbadhack	Mon Jul 10 17:23:07 2000
+++ procps-2.0.7/ps/common.h	Thu Aug  8 10:34:49 2002
@@ -280,6 +280,7 @@
 extern int             header_gap;
 extern int             header_type; /* none, single, multi... */
 extern int             include_dead_children;
+extern int             include_threads;
 extern int             lines_to_next_header;
 extern int             max_line_width;
 extern const char     *namelist_file;
--- procps-2.0.7/ps/display.c.threadbadhack	Mon Jul 10 22:59:14 2000
+++ procps-2.0.7/ps/display.c	Thu Aug  8 13:23:50 2002
@@ -248,6 +248,89 @@
 /* we rely on the POSIX requirement for zeroed memory */
 static proc_t *processes[32*1024];
 
+/***** return true if the processes are the same thread */
+static int same_thread(proc_t *parent, proc_t *child)
+{
+  int i;
+  
+  if (parent->vm_size != child->vm_size ||
+      parent->vm_rss != child->vm_rss)
+    return 0;
+
+  if (parent->cmdline == NULL ||
+      child->cmdline == NULL)
+    return 0;
+  i = 0;
+  while (parent->cmdline[i] != NULL &&
+	 child->cmdline[i] != NULL){
+    if (strcmp (parent->cmdline[i], child->cmdline[i]) != 0)
+      return 0;
+    i++;
+  }
+  
+  if (parent->cmdline[i] != NULL ||
+      child->cmdline[i] != NULL)
+    return 0;
+
+  return 1;
+}
+
+/***** mark all processes that are threads but not the main thread */
+static void mark_and_process_subthreads(int n){
+  unsigned short *pids;
+  int max_pid, i, parent;
+
+  max_pid = 1;
+  for (i = 0; i < n; i++){
+    if (processes[i]->pid > max_pid) max_pid = processes[i]->pid;
+    if (processes[i]->ppid > max_pid) max_pid = processes[i]->ppid;
+  }
+  pids = malloc(sizeof(unsigned short)*(max_pid+1));
+  if (pids == NULL) return;
+
+  /* Clear pids array */
+  for (i = 0; i <= max_pid; i++){
+    pids[i] = 0;
+  }
+
+  /* Create reverse mapping */
+  for (i = 0; i < n; i++){
+    pids[processes[i]->pid] = i + 1;
+  }
+  
+  for (i = 0; i < n; i++){
+    processes[i]->is_subthread = 0;
+    if (processes[i]->ppid <= 1 ||
+	pids[processes[i]->ppid] == 0)
+      continue;
+    parent = pids[processes[i]->ppid] - 1;
+    if (same_thread(processes[parent], processes[i])){
+	processes[i]->is_subthread = 1;
+    }
+  }
+
+  /* Propagagate cpu time from subthreads to master thread */
+  for (i = 0; i < n; i++){
+    if (!processes[i]->is_subthread)
+      continue;
+    
+    parent = i;
+    do {
+      parent = pids[processes[parent]->ppid] - 1;
+    } while (processes[parent]->is_subthread);
+    
+    processes[parent]->cutime += processes[i]->cutime;
+    processes[parent]->cstime += processes[i]->cstime;
+    processes[parent]->utime += processes[i]->utime;
+    processes[parent]->stime += processes[i]->stime;
+    processes[parent]->pcpu += processes[i]->pcpu;
+    if (processes[parent]->pcpu > 999)
+      processes[parent]->pcpu = 999;
+  }
+  
+  free (pids);
+}
+
 /***** compare function for qsort */
 static int compare_two_procs(const void *a, const void *b){
   sort_node *tmp_list = sort_list;
@@ -264,7 +347,9 @@
 static void show_proc_array(int n){
   proc_t **p = processes;
   while(n--){
-    show_one_proc(*p);
+    if (include_threads ||
+	!(*p)->is_subthread)
+      show_one_proc(*p);
     /* if(p->cmdline) free(p->cmdline); */   /* this crashes */
     /* if(p->environ) free(p->environ); */   /* this crashes */
     /* memset(*p, '%', sizeof(proc_t)); */ /* debug */
@@ -283,7 +368,9 @@
     else             forest_prefix[level-1] = 'L';
     forest_prefix[level] = '\0';
   }
-  show_one_proc(processes[self]);  /* first show self */
+  if (include_threads ||
+      !processes[self]->is_subthread)
+    show_one_proc(processes[self]);  /* first show self */
   /* if(p->cmdline) free(p->cmdline); */   /* this crashes */
   /* if(p->environ) free(p->environ); */   /* this crashes */
   /* memset(*p, '%', sizeof(proc_t)); */ /* debug */
@@ -338,8 +425,12 @@
   }
   closeproc(ptp);
   if(!n) return;  /* no processes */
+
+  if(!include_threads) mark_and_process_subthreads(n);
+
   if(forest_type) prep_forest_sort();
   qsort(processes, n, sizeof(proc_t*), compare_two_procs);
+
   if(forest_type) show_forest(n);
   else show_proc_array(n);
 }
@@ -393,7 +484,7 @@
   init_output(); /* must be between parser and output */
   check_headers();
   if (!need_wchan || open_psdb(namelist_file)) wchan_is_number = 1;
-  if(forest_type || sort_list) fancy_spew(); /* sort or forest */
+  if(forest_type || sort_list || !include_threads) fancy_spew(); /* sort or forest */
   else simple_spew(); /* no sort, no forest */
   show_one_proc((proc_t *)-1); /* no output yet? */
   return 0;
--- procps-2.0.7/ps/global.c.threadbadhack	Fri Nov  5 00:56:29 1999
+++ procps-2.0.7/ps/global.c	Thu Aug  8 10:34:49 2002
@@ -60,6 +60,7 @@
 int             header_gap = -1;
 int             header_type = -1;
 int             include_dead_children = -1;
+int             include_threads = -1;
 int             lines_to_next_header = -1;
 int             max_line_width = 666;
 const char     *namelist_file = (const char *)0xdeadbeef;
@@ -330,6 +331,7 @@
   selection_list        = NULL;
   simple_select         = 0;
   sort_list             = NULL;
+  include_threads       = 0;
   unix_f_option         = 0;
   use_aix_codes         = 0;
   user_is_number        = 0;
--- procps-2.0.7/ps/parser.c.threadbadhack	Thu Aug  8 10:34:49 2002
+++ procps-2.0.7/ps/parser.c	Thu Aug  8 10:34:49 2002
@@ -352,7 +352,7 @@
       break;
     case 'm':
       trace("-m shows threads.\n");
-      return "Thread display not implemented.";
+      include_threads = 1;
       break;
     case 'n': /* end */
       trace("-n sets namelist file.\n");
@@ -591,7 +591,7 @@
         defer_sf_option("pmem", SF_B_m);
         break;
       }
-      return "Thread display not implemented.";
+      include_threads = 1;
       break;
     case 'n':
       trace("n Numeric output for WCHAN and USER replaced by UID\n");
--- procps-2.0.7/top.1.threadbadhack	Thu Jul 13 22:19:21 2000
+++ procps-2.0.7/top.1	Thu Aug  8 10:34:49 2002
@@ -88,6 +88,8 @@
 .It c
 display command line instead of the command name only. The default
 behaviour has been changed as this seems to be more useful.
+.It H
+Show all threads for a processes.
 .It n
 Number of iterations. Update the display this number of times and then exit.
 .It b
--- procps-2.0.7/top.c.threadbadhack	Thu Aug  8 10:34:49 2002
+++ procps-2.0.7/top.c	Thu Aug  8 13:23:41 2002
@@ -164,6 +164,9 @@
 	  case 'i':
 	    Noidle = 1;
 	    break;
+	  case 'H':
+	    show_threads = 1;
+	    break;
 	  case 'm':
 	    show_memory = 0;
 	    header_lines -= 2;
@@ -1136,6 +1139,88 @@
     PUTP(top_clrtoeol);
 }
 
+/***** return true if the processes are the same thread */
+static int same_thread(proc_t *parent, proc_t *child)
+{
+  int i;
+  
+  if (parent->vm_size != child->vm_size ||
+      parent->vm_rss != child->vm_rss)
+    return 0;
+
+  if (parent->cmdline == NULL ||
+      child->cmdline == NULL)
+    return 0;
+  i = 0;
+  while (parent->cmdline[i] != NULL &&
+	 child->cmdline[i] != NULL){
+    if (strcmp (parent->cmdline[i], child->cmdline[i]) != 0)
+      return 0;
+    i++;
+  }
+  
+  if (parent->cmdline[i] != NULL ||
+      child->cmdline[i] != NULL)
+    return 0;
+
+  return 1;
+}
+
+/***** mark all processes that are threads but not the main thread */
+static void mark_and_process_subthreads(proc_t **processes){
+  unsigned short *pids;
+  int max_pid, i, parent;
+  
+  max_pid = 1;
+  for (i = 0; processes[i]->pid != -1; i++){
+    if (processes[i]->pid > max_pid) max_pid = processes[i]->pid;
+    if (processes[i]->ppid > max_pid) max_pid = processes[i]->ppid;
+  }
+  pids = xmalloc(sizeof(unsigned short)*(max_pid+1));
+  if (pids == NULL) return;
+
+  /* Clear pids array */
+  for (i = 0; i <= max_pid; i++){
+    pids[i] = 0;
+  }
+
+  /* Create reverse mapping */
+  for (i = 0; processes[i]->pid != -1; i++){
+    pids[processes[i]->pid] = i + 1;
+  }
+  
+  for (i = 0; processes[i]->pid != -1; i++){
+    processes[i]->is_subthread = 0;
+    if (processes[i]->ppid <= 1 ||
+	pids[processes[i]->ppid] == 0)
+      continue;
+    parent = pids[processes[i]->ppid] - 1;
+    if (same_thread(processes[parent], processes[i])){
+	processes[i]->is_subthread = 1;
+    }
+  }
+  
+  /* Propagagate cpu time from subthreads to master thread */
+  for (i = 0; processes[i]->pid != -1; i++){
+    if (!processes[i]->is_subthread)
+      continue;
+    
+    parent = i;
+    do {
+      parent = pids[processes[parent]->ppid] - 1;
+    } while (processes[parent]->is_subthread);
+    
+    processes[parent]->cutime += processes[i]->cutime;
+    processes[parent]->cstime += processes[i]->cstime;
+    processes[parent]->utime += processes[i]->utime;
+    processes[parent]->stime += processes[i]->stime;
+    /* Don't do processes[parent]->pcpu here, as it's not calculated yet */
+  }
+  
+  free (pids);
+}
+
+
 /*
  * This is the real program!  Read process info and display it.
  * One could differentiate options of readproctable2, perhaps it
@@ -1157,6 +1242,8 @@
 	if (monpids_index)
 	    proc_flags |= PROC_PID;
 	p_table=readproctab2(proc_flags, p_table, monpids);
+	if (!show_threads)
+	  mark_and_process_subthreads (p_table);
 	elapsed_time = get_elapsed_time();
 	do_stats(p_table, elapsed_time, 0);
 	sleep(1);
@@ -1173,6 +1260,9 @@
 	putchar('\n');
     }
     p_table=readproctab2(proc_flags, p_table, monpids);
+    /* Mark and process the subthreads before sorting */
+    if (!show_threads)
+      mark_and_process_subthreads (p_table);
     /* Immediately find out the elapsed time for the frame. */
     elapsed_time = get_elapsed_time();
     /* Display the system stats, calculate percent CPU time
@@ -1201,7 +1291,8 @@
 
 	if ( (!Noidle || (stat != 'S' && stat != 'Z')) &&
 	     ( (CurrUser[0] == '\0') ||
-	      (!strcmp((char *)CurrUser,p_table[count]->euser) ) ) ) {
+	      (!strcmp((char *)CurrUser,p_table[count]->euser) ) ) &&
+	     (show_threads || !p_table[count]->is_subthread)) {
 
 	    /*
 	     * Show task info.
@@ -1590,11 +1681,19 @@
 	PUTP(md);
 	fputs(Noidle ? "on" : "off", stdout);
 	PUTP(me);
+	fputs("; show threads ", stdout);
+	PUTP(md);
+	fputs(show_threads ? "on" : "off", stdout);
+	PUTP(me);
 	fputs("\n\n", stdout);
 	printf("%s\n\nPress any key to continue", Secure ? SECURE_HELP_SCREEN : HELP_SCREEN);
 	if (!Batch) tcsetattr(0, TCSANOW, &Rawtty);
 	(void) getchar();
 	break;
+      case 'H':
+	show_threads = !show_threads;
+	SHOWMESSAGE(("Show threads %s", show_threads ? "on" : "off"));
+	break;
       case 'i':
 	Noidle = !Noidle;
 	SHOWMESSAGE(("No-idle mode %s", Noidle ? "on" : "off"));
@@ -1770,6 +1869,8 @@
 		    fprintf(fp, "%c", 'c');
 		if (Noidle)
 		    fprintf(fp, "%c", 'i');
+		if (show_threads)
+		    fprintf(fp, "%c", 'H');
 		if (!show_memory)
 		    fprintf(fp, "%c", 'm');
 		if (!show_loadav)
--- procps-2.0.7/top.h.threadbadhack	Thu Jul 13 22:19:22 2000
+++ procps-2.0.7/top.h	Thu Aug  8 10:34:49 2002
@@ -107,6 +107,7 @@
 int show_memory = 1;   /* show memory summary */
 int show_loadav = 1;   /* show load average and uptime */
 int show_cmd = 1;      /* show command name instead of commandline */
+int show_threads  = 0; /* Show threads */
 int monpids_max = 20;
 pid_t monpids[11] = { 0 };
 int monpids_index = 0;
@@ -171,6 +172,7 @@
 fF\tadd and remove fields\n\
 oO\tChange order of displayed fields\n\
 h or ?\tPrint this list\n\
+H\tToggle dispplay of threads\n\
 S\tToggle cumulative mode\n\
 i\tToggle display of idle proceses\n\
 I\tToggle between Irix and Solaris views (SMP-only)\n\
@@ -197,6 +199,7 @@
 ^L\tRedraw the screen\n\
 fF\tadd and remove fields\n\
 h or ?\tPrint this list\n\
+H\tToggle dispplay of threads\n\
 S\tToggle cumulative mode\n\
 i\tToggle display of idle proceses\n\
 c\tToggle display of command name/line\n\
