added observed cost model.
[dyninst.git] / paradyn / src / DMthread / DMappContext.C
1 /*
2  * DMappConext.C: application context class for the data manager thread.
3  *
4  * $Log: DMappContext.C,v $
5  * Revision 1.29  1994/07/05 03:27:15  hollings
6  * added observed cost model.
7  *
8  * Revision 1.28  1994/07/02  01:43:08  markc
9  * Removed all uses of type aggregation from enableDataCollection.
10  * The metricInfo structure now contains the aggregation operator.
11  *
12  * Revision 1.27  1994/06/29  02:55:55  hollings
13  * fixed code to remove instrumenation when done with it.
14  *
15  * Revision 1.26  1994/06/23  19:26:20  karavan
16  * added option for core dump of all processes with pid=-1 in coreProcess
17  * command.
18  *
19  * Revision 1.25  1994/06/17  22:07:57  hollings
20  * Added code to provide upcall for resource batch mode when a large number
21  * of resources is about to be added.
22  *
23  * Revision 1.24  1994/06/14  15:21:34  markc
24  * Set the aggOp field in metricInstance so the metric can choose from one of
25  * four types of aggregation (max, min, sum, avg).  The aggregation is done in
26  * aggregateSample.C
27  *
28  * Revision 1.23  1994/06/02  23:25:17  markc
29  * Added virtual function 'handle_error' to pardynDaemon class which uses the
30  * error handling features that igen provides.
31  *
32  * Revision 1.22  1994/05/30  19:23:58  hollings
33  * Corrected call to change state for continue to be appRunning not appPaused.
34  *
35  * Revision 1.21  1994/05/23  20:28:04  karavan
36  * fixed return values for addExecutable
37  *
38  * Revision 1.20  1994/05/17  00:17:06  hollings
39  * Made sure we did the correct thing on a callErrr.
40  *
41  * Revision 1.19  1994/05/11  18:45:37  markc
42  * Put code in addExecutable to assign the machine name for paradynDaemons
43  * that are started on the local host.
44  *
45  * Revision 1.18  1994/05/10  03:57:35  hollings
46  * Changed data upcall to return array of buckets.
47  *
48  * Revision 1.17  1994/05/09  20:56:18  hollings
49  * added changeState callback.
50  *
51  * Revision 1.16  1994/04/20  15:30:09  hollings
52  * Added error numbers.
53  * Added data manager function to get histogram buckets.
54  *
55  * Revision 1.15  1994/04/18  22:28:30  hollings
56  * Changes to create a canonical form of a resource list.
57  *
58  * Revision 1.14  1994/03/31  01:42:19  markc
59  * Added pauseProcess, continueProcess member functions.
60  *
61  * Revision 1.13  1994/03/25  22:59:28  hollings
62  * Made the data manager tolerate paraynd's dying.
63  *
64  * Revision 1.12  1994/03/24  16:41:18  hollings
65  * Added support for multiple paradynd's at once.
66  *
67  * Revision 1.11  1994/03/22  21:02:53  hollings
68  * Made it possible to add new processes (& paradynd's) via addExecutable.
69  *
70  * Revision 1.10  1994/03/20  01:49:46  markc
71  * Gave process structure a buffer to allow multiple writers.  Added support
72  * to register name of paradyn daemon.  Changed addProcess to return type int.
73  *
74  * Revision 1.9  1994/03/08  17:39:31  hollings
75  * Added foldCallback and getResourceListName.
76  *
77  * Revision 1.8  1994/03/01  21:24:50  hollings
78  * removed call to print all metrics.
79  *
80  * Revision 1.7  1994/02/25  20:58:10  markc
81  * Added support for storing paradynd's pids.
82  *
83  * Revision 1.6  1994/02/24  04:36:29  markc
84  * Added an upcall to dyninstRPC.I to allow paradynd's to report information at
85  * startup.  Added a data member to the class that igen generates.
86  * Make depend differences due to new header files that igen produces.
87  * Added support to allow asynchronous starts of paradynd's.  The dataManager has
88  * an advertised port that new paradynd's can connect to.
89  *
90  * Revision 1.5  1994/02/09  22:35:29  hollings
91  * added debugging code to print Hash table.
92  *
93  * Revision 1.4  1994/02/08  21:05:54  hollings
94  * Found a few pointer problems.
95  *
96  * Revision 1.3  1994/02/05  23:14:00  hollings
97  * Made sure we didn't return an mi when the enable failed.
98  *
99  * Revision 1.2  1994/02/02  00:42:31  hollings
100  * Changes to the Data manager to reflect the file naming convention and
101  * to support the integration of the Performance Consultant.
102  *
103  * Revision 1.1  1994/01/28  01:34:15  hollings
104  * The initial version of the Data Management thread.
105  *
106  *
107  */
108 #include <assert.h>
109 extern "C" {
110 #include <math.h>
111 double   quiet_nan(int unused);
112 #include <malloc.h>
113 #include "thread/h/thread.h"
114 }
115
116 #include "dataManager.h"
117 #include "dyninstRPC.CLNT.h"
118 #include "DMinternals.h"
119
120 List<performanceStream*> applicationContext::streams;
121
122 void paradynDdebug(int pid)
123 {
124 }
125
126 String_Array convertResourceList(resourceList *rl)
127 {
128     String_Array ret;
129
130     ret.count = rl->getCount();
131     ret.data = rl->convertToStringList();
132     return(ret);
133 }
134
135 // called when a program is started external to paradyn and
136 // must inform paradyn that it exists
137 int applicationContext::addRunningProgram (int pid,
138                                            int argc,
139                                            char **argv,
140                                            paradynDaemon *daemon)
141 {
142         executable *exec;
143
144         exec = new executable (pid, argc, argv, daemon);
145         programs.add(exec);
146         return 0;
147 }
148
149
150 //
151 // add a new paradyn daemon
152 // called when a new paradynd contacts the advertised socket
153 //
154 int applicationContext::addDaemon (int new_fd)
155 {
156   paradynDaemon *new_daemon;
157
158   new_daemon = new paradynDaemon (new_fd, NULL, NULL);
159   // TODO - setup machine, program, login?
160
161   msg_bind (new_daemon->fd, TRUE);
162
163   // TODO - do I need the pid for this ?
164   // The pid is reported later in an upcall
165   // paradynDdebug (new_daemon->pid);
166
167   daemons.add(new_daemon);
168
169   return (0);
170 }
171
172 //
173 // Dispose of daemon state.
174 //    This is called because someone wants to kill a daemon, or the daemon
175 //       died and we need to cleanup after it.
176 //
177 void applicationContext::removeDaemon(paradynDaemon *d, Boolean informUser)
178 {
179     executable *e;
180     List<executable*> progs;
181
182     if (informUser) {
183         printf("paradynd (pid %d) had died\n", d->pid);
184         printf("paradyn Error #5\n");
185     }
186
187     d->dead = TRUE;
188 #ifdef notdef
189     daemons.remove(d);
190
191     //
192     // Delete executables running on the dead paradyn daemon.
193     //
194     for (progs = programs; e = *progs; progs++) {
195        if (e->controlPath == d) {
196            programs.remove(e);
197            delete(e);
198        }
199     }
200
201 #endif
202
203     // tell the thread package to ignore the fd to the daemon.
204     msg_unbind(d->fd);
205 }
206
207 //
208 // add a new executable (binary) to a program.
209 //
210 int applicationContext::addExecutable(char  *machine,
211                                   char *login,
212                                   char *program,
213                                   int argc,
214                                   char **argv)
215 {
216      int pid;
217      executable *exec;
218      paradynDaemon *daemon;
219      String_Array programToRun;
220      List<paradynDaemon*> curr;
221      char local[50];
222
223     // null machine & login are mapped empty strings to keep strcmp happy.
224     if (!machine) 
225       {
226          if (gethostname(local, 49)) assert(0);
227          machine = strdup(local);
228          assert(machine);
229       }
230     if (!login) login = "";
231
232     // find out if we have a paradynd on this machine+login+paradynd
233     for (curr=daemons, daemon = NULL; *curr; curr++) {
234         if (!strcmp((*curr)->machine, machine) &&
235             !strcmp((*curr)->login, login) &&
236             !strcmp((*curr)->program, program)) {
237             daemon = *curr;
238         }
239     }
240
241     // nope start one.
242     if (!daemon) {
243         daemon = new paradynDaemon(machine, login, program, NULL, NULL);
244         if (daemon->fd < 0) {
245             printf("unable to start paradynd: %s\n", program);
246             printf("paradyn Error #6\n");
247             return(-1);
248         }
249         daemons.add(daemon);
250         msg_bind(daemon->fd, TRUE);
251         daemon->my_pid = getpid();
252         paradynDdebug(daemon->pid);
253     }
254
255     programToRun.count = argc;
256     programToRun.data = argv;
257
258     startResourceBatchMode();
259     pid = daemon->addExecutable(argc, programToRun);
260     endResourceBatchMode();
261
262     // did the application get started ok?
263     if (pid > 0 && !daemon->did_error_occur()) {
264         // TODO
265         fprintf (stderr, "PID is %d\n", pid);
266         exec = new executable(pid, argc, argv, daemon);
267         programs.add(exec);
268         return(0);
269     } else {
270         return(-1);
271     }
272 }
273
274 //
275 // Indicate if at least one application has been defined.
276 //
277 Boolean applicationContext::applicationDefined() {
278     return(programs.count() != 0);
279 }
280
281 //
282 // start the programs running.
283 //
284 Boolean applicationContext::startApplication()
285 {
286     executable *exec;
287     List<executable*> curr;
288
289     for (curr = programs; exec = *curr; curr++) {
290         exec->controlPath->startProgram(exec->pid);
291     }
292     return(TRUE);
293 }
294
295 //
296 // pause all processes.
297 //
298 Boolean applicationContext::pauseApplication()
299 {
300     paradynDaemon *dm;
301     performanceStream* s;
302     List<paradynDaemon*> curr;
303     List<performanceStream*> currStreams;
304
305     for (curr = daemons; dm = *curr; curr++) {
306         dm->pauseApplication();
307     }
308
309     // tell perf streams about change.
310     for (currStreams = streams; s = *currStreams; currStreams++) {
311         s->callStateFunc(appPaused);
312     }
313
314     return(TRUE);
315 }
316
317 //
318 // pause one processes.
319 //
320 Boolean applicationContext::pauseProcess(int pid)
321 {
322     executable *exec;
323     List<executable*> curr;
324
325     for (curr = programs; exec = *curr; curr++) {
326         if (exec->pid == pid) break; 
327     }
328     if (exec) {
329         exec->controlPath->pauseProgram(exec->pid);
330         return(TRUE); 
331     } else
332         return (FALSE);
333 }
334
335 //
336 // continue all processes.
337 //
338 Boolean applicationContext::continueApplication()
339 {
340     paradynDaemon *dm;
341     performanceStream* s;
342     List<paradynDaemon*> curr;
343     List<performanceStream*> currStreams;
344
345     for (curr = daemons; dm = *curr; curr++) {
346         dm->continueApplication();
347     }
348
349     // tell perf streams about change.
350     for (currStreams = streams; s = *currStreams; currStreams++) {
351         s->callStateFunc(appRunning);
352     }
353
354     return(TRUE);
355 }
356
357 //
358 // continue one processes.
359 //
360 Boolean applicationContext::continueProcess(int pid)
361 {
362     executable *exec;
363     List<executable*> curr;
364
365     for (curr = programs; exec = *curr; curr++) {
366         if (exec->pid == pid) break;
367     }
368     if (exec) {
369         exec->controlPath->continueProgram(exec->pid);
370         return(TRUE); 
371     } else
372         return (FALSE);
373 }
374
375 //
376 // detach the paradyn tool from a running program.  This should clean all
377 //   of the dynamic instrumentation that has been inserted.
378 //
379 Boolean applicationContext::detachApplication(Boolean)
380 {
381     executable *exec;
382     List<executable*> curr;
383
384     for (curr = programs; exec = *curr; curr++) {
385         exec->controlPath->continueProgram(exec->pid);
386     }
387     return(TRUE);
388 }
389
390 //
391 // print the status of each process.  This is used mostly for debugging.
392 //
393 void applicationContext::printStatus()
394 {
395     String status;
396     executable *exec;
397     List<executable*> curr;
398
399     for (curr = programs; exec = *curr; curr++) {
400         status = exec->controlPath->getStatus(exec->pid);
401         if (!exec->controlPath->did_error_occur()) {
402             printf("%s\n", status);
403         }
404     }
405 }
406
407 //
408 // Cause the passed process id to dump a core file.  This is also used for
409 //    debugging.
410 // If pid = -1, all processes will dump core files.
411 //
412 void applicationContext::coreProcess(int pid)
413 {
414     executable *exec;
415     List<executable*> curr;
416
417     for (curr = programs; exec = *curr; curr++) {
418         if ((exec->pid == pid) || (pid == -1)) {
419             exec->controlPath->coreProcess(exec->pid);
420             printf("found process and coreing it\n");
421         }
422     }
423   }
424
425 //
426 // Find out what metrics are available.  This just returns their names.
427 //
428 String_Array applicationContext::getAvailableMetrics()
429 {
430     int i;
431     String_Array names;
432     HTable<metric*> cm;
433
434     names.count = metric::allMetrics.count();
435     names.data = (String *) malloc(sizeof(String) * names.count);
436     for (cm=metric::allMetrics,i=0; *cm; cm++,i++) {
437        names.data[i] = (*cm)->getName();
438        assert(names.data[i]);
439     }
440     assert(i==names.count);
441     return(names);
442 }
443
444 //
445 // look up the metric info about the passed metric.
446 //
447 metric *applicationContext::findMetric(char *name)
448 {
449     name = metric::names.findAndAdd(name);
450     return(metric::allMetrics.find(name));
451     return(NULL);
452 }
453
454 //
455 // Get the expected delay (as a fraction of the running program) for the passed
456 //   resource list (focus) and metric.
457 //
458 float applicationContext::getPredictedDataCost(resourceList *rl, metric *m)
459 {
460     double val, max;
461     String_Array ra;
462     paradynDaemon *daemon;
463     List<paradynDaemon*> curr;
464
465     ra = convertResourceList(rl);
466     max = 0.0;
467     for (curr = daemons; *curr; curr++) {
468         daemon = *curr;
469         val = daemon->getPredictedDataCost(ra, m->getName());
470         if (val > max) val = max;
471     }
472     return(max);
473 }
474
475 void histDataCallBack(sampleValue *buckets,
476                       int count,
477                       int first,
478                       void *arg)
479 {
480     metricInstance *mi;
481     performanceStream *ps;
482     List<performanceStream*> curr;
483
484     mi = (metricInstance *) arg;
485     for (curr = mi->users; ps = *curr; curr++) {
486         ps->callSampleFunc(mi, buckets, count, first);
487     }
488 }
489
490 void histFoldCallBack(timeStamp width, void *arg)
491 {
492     metricInstance *mi;
493     performanceStream *ps;
494     List<performanceStream*> curr;
495
496     mi = (metricInstance *) arg;
497     if (mi->count) {
498         for (curr = mi->users; ps = *curr; curr++) {
499             ps->callFoldFunc(width);
500         }
501     }
502 }
503
504 //
505 // Start collecting data about the passed resource list (focus) and metric.
506 //    The returned metricInstance is used to provide a unique handle for this
507 //    metric/focus pair.
508 //
509 metricInstance *applicationContext::enableDataCollection(resourceList *rl, 
510                                                          metric *m)
511 {
512     int id;
513     char *name;
514     component *comp;
515     String_Array ra;
516     Boolean foundOne;
517     metricInstance *mi;
518     paradynDaemon *daemon;
519     List<paradynDaemon*> curr;
520
521     ra = convertResourceList(rl);
522
523     // 
524     // for each daemon request the data to be enabled.
525     //
526     mi = new metricInstance(rl, m);
527     foundOne = FALSE;
528     for (curr = daemons; daemon = *curr; curr++) {
529         id = daemon->enableDataCollection(ra, m->getName());
530         if (id > 0 && !daemon->did_error_occur()) {
531             comp = new component(*curr, id, mi);
532             mi->components.add(comp, (void *) *curr);
533             mi->parts.add(&comp->sample, (void *) *curr);
534             foundOne = TRUE;
535         }
536     }
537     if (foundOne) {
538         mi->data = new Histogram(m->getStyle(), 
539                                  histDataCallBack, 
540                                  histFoldCallBack, 
541                                  (void *) mi);
542         name = rl->getCanonicalName();
543         m->enabledCombos.add(mi, (void*) name);
544         mi->count = 1;
545         return(mi);
546     } else {
547         delete(mi);
548         return(NULL);
549     }
550 }
551
552 //
553 // This actuals stops the data from being collected.
554 //
555 void applicationContext::disableDataCollection(metricInstance *mi)
556 {
557     metric *m;
558     char *name;
559     component *c;
560     List<component*> curr;
561
562     m = mi->met;
563     name = mi->focus->getCanonicalName();
564     m->enabledCombos.remove(name);
565     for (curr = mi->components; c = *curr; curr++) {
566         delete(c);
567     }
568     delete(mi);
569 }
570
571 void applicationContext::startResourceBatchMode() 
572 {
573     List<performanceStream*> curr;
574     for (curr = streams; *curr; curr++) {
575         (*curr)->callResourceBatchFunc(batchStart);
576     }
577 }
578
579 void applicationContext::endResourceBatchMode() 
580 {
581     List<performanceStream*> curr;
582     for (curr = streams; *curr; curr++) {
583         (*curr)->callResourceBatchFunc(batchEnd);
584     }
585 }
586