added new call registerValidVisis. This single call from Visi Manager to
[dyninst.git] / paradyn / src / UIthread / UIpublic.C
1 /*
2  * Copyright (c) 1996 Barton P. Miller
3  * 
4  * We provide the Paradyn Parallel Performance Tools (below
5  * described as Paradyn") on an AS IS basis, and do not warrant its
6  * validity or performance.  We reserve the right to update, modify,
7  * or discontinue this software at any time.  We shall have no
8  * obligation to supply such updates or modifications or any other
9  * form of support to you.
10  * 
11  * This license is for research uses.  For such uses, there is no
12  * charge. We define "research use" to mean you may freely use it
13  * inside your organization for whatever purposes you see fit. But you
14  * may not re-distribute Paradyn or parts of Paradyn, in any form
15  * source or binary (including derivatives), electronic or otherwise,
16  * to any other organization or entity without our permission.
17  * 
18  * (for other uses, please contact us at paradyn@cs.wisc.edu)
19  * 
20  * All warranties, including without limitation, any warranty of
21  * merchantability or fitness for a particular purpose, are hereby
22  * excluded.
23  * 
24  * By your use of Paradyn, you understand and agree that we (or any
25  * other person or entity with proprietary rights in Paradyn) are
26  * under no obligation to provide either maintenance services,
27  * update services, notices of latent defects, or correction of
28  * defects for Paradyn.
29  * 
30  * Even if advised of the possibility of such damages, under no
31  * circumstances shall we (or any other person or entity with
32  * proprietary rights in the software licensed hereunder) be liable
33  * to you or any third party for direct, indirect, or consequential
34  * damages of any character regardless of type of action, including,
35  * without limitation, loss of profits, loss of use, loss of good
36  * will, or computer failure or malfunction.  You agree to indemnify
37  * us (and any other person or entity with proprietary rights in the
38  * software licensed hereunder) for any and all liability it may
39  * incur to third parties resulting from your use of Paradyn.
40  */
41
42 /*
43  * UIpublic.C : exported services of the User Interface Manager thread 
44  *              of Paradyn
45  *
46  */
47  
48 /* $Log: UIpublic.C,v $
49 /* Revision 1.62  1997/06/02 19:41:57  karavan
50 /* added new call registerValidVisis.  This single call from Visi Manager to
51 /* UI thread at startup registers all valid visis as specified in a config
52 /* file, and replaces use of synchronous VM->VMAvailableVisis().
53 /*
54  * Revision 1.61  1997/05/02 04:43:49  karavan
55  * added new functionality to support "SAVE" feature.
56  *
57  * added support to use standard tcl autoload feature for development use.
58  *
59  * Revision 1.60  1996/11/26 16:06:54  naim
60  * Fixing asserts - naim
61  *
62  * Revision 1.59  1996/10/31 08:19:16  tamches
63  * UIM::enablePauseOrRun()
64  *
65  * Revision 1.58  1996/08/16 21:06:48  tamches
66  * updated copyright for release 1.1
67  *
68  * Revision 1.57  1996/05/07 18:05:56  newhall
69  * added threadExiting routine
70  *
71  * Revision 1.56  1996/05/01  20:54:20  tamches
72  * added DAGinactivateEntireSearch
73  *
74  * Revision 1.55  1996/05/01 14:07:54  naim
75  * Multiples changes in UI to make call to requestNodeInfoCallback async.
76  * (UI<->PC) - naim
77  *
78  * Revision 1.54  1996/04/19  18:28:17  naim
79  * Adding a procedure that will be called when we want to add a new process,
80  * as it is done using the "paradyn process" command - naim
81  *
82  * Revision 1.53  1996/04/18  20:46:35  tamches
83  * new DAGaddBatchOfEdges to correspond with PCthread/PCshg.C changes
84  *
85  * Revision 1.52  1996/04/16 18:37:27  karavan
86  * fine-tunification of UI-PC batching code, plus addification of some
87  * Ari-like verbification commentification.
88  *
89  * Revision 1.51  1996/04/13 04:39:39  karavan
90  * better implementation of batching for edge requests
91  *
92  * Revision 1.50  1996/04/09 19:25:07  karavan
93  * added batch mode to cut down on shg redraw time.
94  *
95  * Revision 1.49  1996/04/07 21:17:12  karavan
96  * changed new phase notification handling; instead of being notified by the
97  * data manager, the UI is notified by the performance consultant.  This prevents
98  * a race condition.
99  *
100  */
101
102 #include <stdio.h>
103 #include <string.h>
104 #include <stdarg.h>
105
106 #include "tcl.h"
107 #include "tk.h"
108
109 #include "UI.thread.CLNT.h"
110 #include "UI.thread.SRVR.h"
111 #include "UI.thread.h"
112 #include "thread/h/thread.h"
113 #include "UIglobals.h"
114 #include "../pdMain/paradyn.h"
115
116 #include "shgPhases.h"
117 #include "shgTcl.h"
118
119  /* globals for metric resource selection */
120 vector<metric_focus_pair> uim_VisiSelections; // keep this one
121
122 void
123 UIMUser::chosenMetricsandResources
124           (chooseMandRCBFunc cb,
125            vector<metric_focus_pair> *pairList)
126 {
127   (cb) (pairList);
128 }
129         
130 // 
131 // Startup File
132 void 
133 UIM::readStartupFile(const char *script)
134 {
135   if (script != NULL) {   // script specified on paradyn command line
136     string tcommand = string("source \"") + string(script) + "\"";
137
138     if (Tcl_Eval (interp, tcommand.string_of()) == TCL_ERROR)
139       uiMgr->showError(24, "");
140   }
141 }
142
143 // ****************************************************************
144 // Error Message Display Service 
145 // ****************************************************************
146
147 void
148 UIM::showError(int errCode, const char *errString)
149 {
150     // errString -- custom error info string to be printed
151     // Note that UIthread code can make the call to the tcl
152     // routine "showError" directly; no need to call us.
153
154     string tcommand = string("showError ") + string(errCode) + string(" ");
155     if (errString == NULL || errString[0] == '\0')
156        tcommand += string("\"\"");
157     else
158        tcommand += string("{") + string(errString) + string("}");
159     myTclEval(interp, tcommand);
160 }
161
162 // ****************************************************************
163 // Status Line Display Service
164 // ****************************************************************
165
166 void
167 UIM::updateStatus(status_line *status, const char *msg)
168 {
169   status->message(msg);
170 }
171
172 void UIM::enablePauseOrRun() {
173    extern void enablePAUSEorRUN(); // paradyn.tcl.C
174    enablePAUSEorRUN();
175 }
176
177 // ****************************************************************
178 // Metrics and Resources 
179 // ****************************************************************
180
181 unsigned metric_name_hash(const unsigned &metid) {return metid;}
182 dictionary_lite<unsigned, string> UI_all_metric_names(metric_name_hash);
183    // met-id (not index!) to name
184 bool UI_all_metrics_set_yet = false;
185
186 void 
187 UIM::chooseMetricsandResources(chooseMandRCBFunc cb,
188                                vector<metric_focus_pair> *pairList)
189 {
190       // store record with unique id and callback function
191   UIMMsgTokenID++;
192   int newptr;
193   Tcl_HashEntry *entryPtr = Tcl_CreateHashEntry (&UIMMsgReplyTbl,
194                                                  (char *)UIMMsgTokenID, 
195                                                  &newptr);
196   if (newptr == 0) {
197     uiMgr->showError(21, "");
198     thr_exit(0);
199   }
200
201   unsigned requestingThread = getRequestingThread();
202      // in theory, we can check here whether this (VISI-) thread already
203      // has an outstanding metric request.  But for now, we let code in mets.tcl do this...
204 //  string commandStr = string("winfo exists .metmenunew") + string(requestingThread);
205 //  myTclEval(interp, commandStr);
206 //  int result;
207 //  assert(TCL_OK == Tcl_GetBoolean(interp, interp->result, &result));
208 //  if (result)
209 //     return; // the window is already up for this thread!
210
211   UIMReplyRec *reply = new UIMReplyRec;
212   reply->tid = requestingThread;
213   reply->cb = (showMsgCBFunc) cb;
214   Tcl_SetHashValue (entryPtr, reply);
215
216   if (!UI_all_metrics_set_yet) {
217       vector<met_name_id> *all_mets = dataMgr->getAvailableMetInfo(true);
218       
219       for (unsigned metlcv=0; metlcv < all_mets->size(); metlcv++) {
220          unsigned id  = (*all_mets)[metlcv].id;
221          string &name = (*all_mets)[metlcv].name;
222
223          UI_all_metric_names[id] = name;
224
225          string idString(id);
226          bool aflag;
227          aflag=(Tcl_SetVar2(interp, "metricNamesById", 
228                             idString.string_of(),
229                             name.string_of(), TCL_GLOBAL_ONLY));
230          assert(aflag);
231       }
232       
233       delete all_mets;
234       UI_all_metrics_set_yet = true;
235   }
236
237   // Set metIndexes2Id via "temp"
238   (void)Tcl_UnsetVar(interp, "temp", 0);
239      // ignore result; temp may not have existed
240   vector<met_name_id> *curr_avail_mets_ptr = dataMgr->getAvailableMetInfo(false);
241   vector<met_name_id> &curr_avail_mets = *curr_avail_mets_ptr;
242   unsigned numAvailMets = curr_avail_mets.size();
243   for (unsigned metlcv=0; metlcv < numAvailMets; metlcv++) {
244      string metricIdStr = string(curr_avail_mets[metlcv].id);
245      
246      bool aflag;
247      aflag=(Tcl_SetVar(interp, "temp", metricIdStr.string_of(),
248                        TCL_APPEND_VALUE | TCL_LIST_ELEMENT));
249      assert(aflag);
250   }
251   delete curr_avail_mets_ptr;
252   
253
254   string tcommand("getMetsAndRes ");
255   tcommand += string(UIMMsgTokenID);
256   tcommand += string(" ") + string(requestingThread);
257   tcommand += string(" ") + string(numAvailMets);
258   tcommand += string(" $temp");
259
260   int retVal = Tcl_VarEval (interp, tcommand.string_of(), 0);
261   if (retVal == TCL_ERROR)  {
262     uiMgr->showError (22, "");
263     cerr << interp->result << endl;
264     thr_exit(0);  
265   } 
266 }
267
268 //
269 // called by an exiting thread to notify the UI that it is exiting
270 // this is necessary so that the UI does not try to send a metrics
271 // menuing response to a dead tid
272 //
273 void UIM::threadExiting(){
274
275     thread_t tid = getRequestingThread();
276
277     Tcl_HashSearch *searchPtr = new Tcl_HashSearch;
278     Tcl_HashEntry *entry = Tcl_FirstHashEntry(&UIMMsgReplyTbl,searchPtr);
279
280     // check to see if there is an outstanding metrics menuing request
281     // for this thread, and if so, remove its entry from the table
282     while(entry){
283         UIMReplyRec *msgRec = (UIMReplyRec *)Tcl_GetHashValue(entry);
284         if(msgRec->tid == tid){
285             Tcl_DeleteHashEntry(entry);
286             if(searchPtr) delete searchPtr;
287             return;
288         }
289         entry = Tcl_NextHashEntry(searchPtr);
290     }
291     if(searchPtr) delete searchPtr;
292 }
293
294 // ****************************************************************
295 //  DAG/SHG Display Service Routines
296 // ****************************************************************
297
298 extern shgPhases *theShgPhases;
299
300 // The following two variables tell the shg which phase to try to
301 // activate when _first_ opening the shg window.  We initialize it
302 // to the well-known values for the "current phase" which is
303 // created on startup.
304 int latest_detected_new_phase_id = 1;
305 const char *latest_detected_new_phase_name = "phase_0";
306
307 void UIM::newPhaseNotification (unsigned ph, const char *name, bool with_new_pc) {
308 //   cout << "UI welcome to new_phase_detected" << endl;
309 //   cout << "id=" << ph << endl;
310 //   cout << "name=" << name << endl;
311
312    // For the benefit of the shg, in the event that the shg window
313    // has not yet been opened, with the result that "theShgPhases"
314    // hasn't yet been constructed:
315    extern shgPhases *theShgPhases;
316    if (theShgPhases == NULL) {
317       latest_detected_new_phase_id = ph;
318       //** memory leak
319       latest_detected_new_phase_name = name;
320       //cout << "ui_newPhaseDetected: deferring phase id " << ph << " (" << name << ") since shg window not yet opened" << endl;
321       if (with_new_pc)
322          cout << "can't begin searching the new phase since Perf Consultant window not yet opened" << endl;
323    }
324    else {
325       //cout << "ui_newPhaseDetected: adding the phase now" << endl;
326       bool redraw = theShgPhases->defineNewSearch(ph, name);
327
328       if (with_new_pc) {
329          // the user has requested that we begin searching immediately on this
330          // new phase, as if we had clicked on the "Search" button.  So let's do
331          // the equivalent.  But first, we must switch to the new "screen".
332          bool aflag;
333          aflag=theShgPhases->changeByPhaseId(ph);
334          assert(aflag);
335
336          myTclEval(interp, "shgClickOnSearch");
337             // calls shgSearchCommand (shgTcl.C), which calls activateCurrSearch()
338             // in shgPhases.C
339
340          //cout << "ui_newPhaseDetected: started the new search!" << endl;
341
342          redraw = true;
343       }
344       
345       if (redraw)
346          initiateShgRedraw(interp, true);
347    }
348 }
349
350 void
351 UIM::updateStatusDisplay (int dagid, string *info)
352 {
353    theShgPhases->addToStatusDisplay(dagid, *info);
354    delete info;
355 }
356
357 bool haveSeenFirstGoodShgWid = false;
358
359 bool tryFirstGoodShgWid(Tcl_Interp *interp, Tk_Window topLevelTkWindow) {
360    // called in shgTcl.C
361    // like whereAxis's and barChart's techniques...
362    // Tk_WindowId() returns 0 until the tk window has been mapped for the first
363    // time, which takes a surprisingly long time.  Therefore, this hack is needed.
364
365    if (haveSeenFirstGoodShgWid)
366       return true;
367
368    Tk_Window theTkWindow = Tk_NameToWindow(interp, ".shg.nontop.main.all",
369                                            topLevelTkWindow);
370    assert(theTkWindow);
371
372    if (Tk_WindowId(theTkWindow) == 0)
373       return false; // this happens in practice...that's why this routine is needed
374
375    haveSeenFirstGoodShgWid = true;
376
377    /* *********************************************************** */
378
379    // Why don't we construct "theShgPhases" earlier (perhaps at startup)?
380    // Why do we wait until the shg window has been opened?
381    // Because the constructor requires window names as arguments.
382    theShgPhases = new shgPhases(".shg.titlearea.left.menu.mbar.phase.m",
383                                 ".shg.nontop.main.bottsb",
384                                 ".shg.nontop.main.leftsb",
385                                 ".shg.nontop.labelarea.current",
386                                 ".shg.nontop.textarea.text",
387                                 ".shg.nontop.buttonarea.left.search",
388                                 ".shg.nontop.buttonarea.middle.pause",
389                                 ".shg.nontop.currphasearea.label2",
390                                 interp, theTkWindow);
391    assert(theShgPhases);
392
393    // Now is as good a time as any to define the global phase.
394    const int GlobalPhaseId = 0; // a hardcoded constant
395    (void)theShgPhases->defineNewSearch(GlobalPhaseId,
396                                        "Global Phase");
397
398    // Also add the "current phase", if applicable.
399    // We check "latest_detected_new_phase_id", set by ui_newPhaseDetected (UImain.C)
400    extern int latest_detected_new_phase_id;
401    extern const char *latest_detected_new_phase_name;
402    if (latest_detected_new_phase_id >= 0) {
403       theShgPhases->defineNewSearch(latest_detected_new_phase_id,
404                                     latest_detected_new_phase_name);
405    }
406
407    initiateShgRedraw(interp, true);
408
409    return true;
410 }
411
412 void int2style(int styleid, bool &active, shgRootNode::evaluationState &theEvalState) {
413    if (styleid == 0) {
414       active = false;
415       theEvalState = shgRootNode::es_never;
416    }
417    else if (styleid == 1) {
418       active = false;
419       theEvalState = shgRootNode::es_unknown;
420    }
421    else if (styleid == 2) {
422       active = false;
423       theEvalState = shgRootNode::es_false;
424    }
425    else if (styleid == 3) {
426       active = true;
427       theEvalState = shgRootNode::es_true;
428    }
429    else if (styleid == 4) {
430       active = true;
431       theEvalState = shgRootNode::es_unknown;
432    }
433    else if (styleid == 5) {
434       active = true;
435       theEvalState = shgRootNode::es_false;
436    }
437    else if (styleid == 6) {
438       active = false;
439       theEvalState = shgRootNode::es_true;
440    }
441    else {
442       cerr << "unrecognized style id " << styleid << endl;
443       exit(5);
444    }
445 }
446
447 shgRootNode::refinement int2refinement(int styleid) {
448    if (styleid == 0)
449       return shgRootNode::ref_where;
450    else if (styleid == 1)
451       return shgRootNode::ref_why;
452
453    cerr << "unrecognized refinement id " << styleid << endl;
454    exit(5);
455 }
456
457 /*  flags: 1 = root
458  *         0 = non-root
459  */
460
461 int 
462 UIM::DAGaddNode(int dagID, unsigned nodeID, int styleID, 
463                 const char *label, const char *shgname, int flags)
464 {
465    const bool isRootNode = (flags == 1);
466    bool active;
467    shgRootNode::evaluationState theEvalState;
468    int2style(styleID, active, theEvalState);
469
470    if (theShgPhases->addNode(dagID, nodeID, active, theEvalState,
471                              label, shgname, isRootNode))
472       // shouldn't we should only be redrawing for a root node?
473       if (isRootNode)
474          initiateShgRedraw(interp, true);
475
476    return 1;
477 }
478
479 void
480 UIM::DAGaddBatchOfEdges (int dagID, vector<uiSHGrequest> *requests,
481                          unsigned numRequests)
482 {
483   // "requests" was allocated (using new) by the producer (PCshg.C code); we
484   // delete it here.
485   bool redraw = false;
486   assert(requests->size() == numRequests); // a sanity check just for fun
487
488   for (unsigned i = 0; i < numRequests; i++) {
489     const uiSHGrequest &curr = (*requests)[i];
490     if (theShgPhases->addEdge(dagID,
491                               curr.srcNodeID, // parent
492                               curr.dstNodeID, // child
493                               int2refinement(curr.styleID),
494                               curr.label,
495                               i==numRequests-1 // rethink only once, at the end
496                               ))
497        redraw = true;
498   }
499
500   delete requests;
501
502   if (redraw)
503      initiateShgRedraw(interp, true); // true --> double buffer
504 }
505
506 int 
507 UIM::DAGaddEdge (int dagID, unsigned srcID, 
508                  unsigned dstID,
509                  int styleID, // why vs. where refinement
510                  const char *label // only used for shadow node; else NULL
511                  )
512 {
513    if (theShgPhases->addEdge(dagID, 
514                              srcID, // parent
515                              dstID, // child
516                              int2refinement(styleID),
517                              label, true)) // true --> rethink
518      initiateShgRedraw(interp, true); // true --> double buffer
519
520    return 1;
521 }
522
523   
524 int 
525 UIM::DAGconfigNode (int dagID, unsigned nodeID, int styleID)
526 {
527    bool active;
528    shgRootNode::evaluationState theEvalState;
529    int2style(styleID, active, theEvalState);
530   
531    if (theShgPhases->configNode(dagID, nodeID, active, theEvalState))
532       initiateShgRedraw(interp, true); // true --> double buffer
533
534    return 1;
535 }
536
537 void UIM::DAGinactivateEntireSearch(int dagID) {
538    if (theShgPhases->inactivateEntireSearch(dagID))
539       initiateShgRedraw(interp, true); // true --> double buffer
540 }
541
542 void UIM::requestNodeInfoCallback(unsigned phaseID, int nodeID, 
543                                   shg_node_info *theInfo, bool ok)
544 {
545   if (ok) {
546     theShgPhases->nodeInformation(phaseID,nodeID,*theInfo);
547     delete theInfo;
548   }
549 }
550
551 // ****************************************************************
552 // MISC 
553 // ****************************************************************
554
555 //
556 // This procedure is used when paradyn create a process after 
557 // reading a configuration file (using option -f).
558 //
559 void UIM::ProcessCmd(string *args)
560 {
561   string command;
562   command = string("paradyn process ") + (*args);
563   if (Tcl_VarEval(interp,command.string_of(),0)==TCL_ERROR) {
564     string msg = string("Tcl interpreter failed in routine UIM::ProcessCmd: ");
565     msg += string((const char *) interp->result);
566     uiMgr->showError(83, P_strdup(msg.string_of()));
567   }  
568   delete args;
569 }
570
571 // initialize list of external visis in the tcl interpreter:
572 //  this routine sets global tcl variables vnames, vnums, vcount
573 //
574 int compare_visi_names (const void *viptr1, const void *viptr2) {
575   const VM_visiInfo *p1 = (const VM_visiInfo *)viptr1;
576   const VM_visiInfo *p2 = (const VM_visiInfo *)viptr2;
577   return strcmp (p1->name.string_of(), p2->name.string_of());
578 }
579
580 void 
581 UIM::registerValidVisis (vector<VM_visiInfo> *via) {
582   int i;
583   int count;
584   Tcl_DString namelist;
585   Tcl_DString numlist;
586   char num[8];
587
588   count = via->size();  
589   via->sort (compare_visi_names);
590   Tcl_DStringInit(&namelist);
591   Tcl_DStringInit(&numlist);
592   
593   for (i = 0; i < count; i++) {
594     Tcl_DStringAppendElement(&namelist, (*via)[i].name.string_of());
595     sprintf (num, "%d", ((*via)[i]).visiTypeId);
596     Tcl_DStringAppendElement(&numlist, num);
597   }
598   Tcl_SetVar (interp, "vnames", Tcl_DStringValue(&namelist), 0);
599   Tcl_SetVar (interp, "vnums", Tcl_DStringValue(&numlist), 0);
600   Tcl_DStringFree (&namelist);
601   Tcl_DStringFree (&numlist);
602   sprintf (num, "%d", count);
603   Tcl_SetVar (interp, "vcount", num, 0);
604   delete via;
605 }
606
607
608
609
610
611 void 
612 UIM::allDataSaved(bool succeeded)
613 {
614   if (succeeded) 
615     ui_status->message("Requested Data Saved");
616   else
617     ui_status->message("Data Save Request Failed");
618 }
619
620 void 
621 UIM::resourcesSaved(bool succeeded)
622 {
623   if (succeeded)
624     ui_status->message("Resource Hierarchies Saved");
625   else
626     ui_status->message("Resource Hierarchy Save Request Failed");
627 }