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