2 * Copyright (c) 1996 Barton P. Miller
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.
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.
18 * (for other uses, please contact us at paradyn@cs.wisc.edu)
20 * All warranties, including without limitation, any warranty of
21 * merchantability or fitness for a particular purpose, are hereby
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.
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.
42 #include "util/h/headers.h"
46 #include "rtinst/h/rtinst.h"
47 #include "rtinst/h/trace.h"
48 #include "util/h/aggregateSample.h"
49 #include "dyninstAPI/src/symtab.h"
50 #include "dyninstAPI/src/pdThread.h"
51 #include "dyninstAPI/src/process.h"
52 #include "dyninstAPI/src/inst.h"
53 #include "dyninstAPI/src/instP.h"
54 #include "dyninstAPI/src/dyninstP.h"
55 #include "dyninstAPI/src/ast.h"
56 #include "dyninstAPI/src/util.h"
57 #include "paradynd/src/comm.h"
58 #include "paradynd/src/internalMetrics.h"
59 #include "paradynd/src/init.h"
60 #include "paradynd/src/perfStream.h"
61 #include "paradynd/src/main.h"
62 #include "dyninstAPI/src/stats.h"
63 #include "paradynd/src/dynrpc.h"
64 #include "paradynd/src/mdld.h"
65 #include "util/h/Timer.h"
66 #include "paradynd/src/showerror.h"
67 #include "paradynd/src/costmetrics.h"
68 #include "paradynd/src/metric.h"
69 #include "util/h/debugOstream.h"
71 // The following vrbles were defined in process.C:
72 extern debug_ostream attach_cerr;
73 extern debug_ostream inferiorrpc_cerr;
74 extern debug_ostream shmsample_cerr;
75 extern debug_ostream forkexec_cerr;
76 extern debug_ostream metric_cerr;
78 extern unsigned inferiorMemAvailable;
79 extern vector<unsigned> getAllTrampsAtPoint(instInstance *instance);
80 static unsigned internalMetricCounterId = 0;
82 static unsigned numOfActCounters_all=0;
83 static unsigned numOfActProcTimers_all=0;
84 static unsigned numOfActWallTimers_all=0;
86 void flush_batch_buffer();
87 void batchSampleData(int mid, double startTimeStamp, double endTimeStamp,
88 double value, unsigned val_weight, bool internal_metric);
90 double currentPredictedCost = 0.0;
92 dictionary_hash <unsigned, metricDefinitionNode*> midToMiMap(uiHash);
93 // maps low-level counter-ids to metricDefinitionNodes
95 unsigned mdnHash(const metricDefinitionNode *&mdn) {
96 return ((unsigned)mdn) >> 2; // assume all addrs are 4-byte aligned
97 // return ((unsigned) mdn);
100 unsigned componentMdnPtrHash(metricDefinitionNode * const &ptr) {
101 // maybe assert that "ptr" isn't for an aggregate mi
102 return string::hash(ptr->getFullName());
106 dictionary_hash<unsigned, metricDefinitionNode*> allMIs(uiHash);
107 dictionary_hash<string, metricDefinitionNode*> allMIComponents(string::hash);
108 vector<internalMetric*> internalMetric::allInternalMetrics;
110 // used to indicate the mi is no longer used.
112 #define MILLION 1000000.0
114 bool mdl_internal_metric_data(const string& metric_name, mdl_inst_data& result) {
115 unsigned size = internalMetric::allInternalMetrics.size();
116 for (unsigned u=0; u<size; u++) {
117 internalMetric *theIMetric = internalMetric::allInternalMetrics[u];
118 if (theIMetric->name() == metric_name) {
119 result.aggregate = theIMetric->aggregate();
120 result.style = theIMetric->style();
125 for (unsigned u2=0; u2< costMetric::allCostMetrics.size(); u2++) {
126 if (costMetric::allCostMetrics[u2]->name() == metric_name) {
127 result.aggregate = costMetric::allCostMetrics[u2]->aggregate();
128 result.style = costMetric::allCostMetrics[u2]->style();
133 return (mdl_metric_data(metric_name, result));
136 // for non-aggregate metrics
137 metricDefinitionNode::metricDefinitionNode(process *p, const string& met_name,
138 const vector< vector<string> >& foc,
139 const vector< vector<string> >& component_foc,
140 const string& component_flat_name, int agg_style)
142 aggOp(agg_style), // CM5 metrics need aggOp to be set
143 inserted_(false), installed_(false), met_(met_name),
144 focus_(foc), component_focus(component_foc),
145 flat_name_(component_flat_name),
147 cumulativeValue_float(0.0),
148 id_(-1), originalCost_(0.0), proc_(p)
152 aflag=mdl_internal_metric_data(met_name, md);
157 // for aggregate metrics
158 metricDefinitionNode::metricDefinitionNode(const string& metric_name,
159 const vector< vector<string> >& foc,
160 const string& cat_name,
161 vector<metricDefinitionNode*>& parts,
163 : aggregate_(true), aggOp(agg_op), inserted_(false), installed_(false),
164 met_(metric_name), focus_(foc),
165 flat_name_(cat_name), components(parts),
167 id_(-1), originalCost_(0.0), proc_(NULL)
169 unsigned p_size = parts.size();
170 for (unsigned u=0; u<p_size; u++) {
171 metricDefinitionNode *mi = parts[u];
172 mi->aggregators += this;
173 mi->samples += aggSample.newComponent();
177 // check for "special" metrics that are computed directly by paradynd
178 // if a cost of an internal metric is asked for, enable=false
179 metricDefinitionNode *doInternalMetric(vector< vector<string> >& canon_focus,
180 vector< vector<string> >& component_canon_focus,
181 string& metric_name, string& flat_name,
182 bool enable, bool& matched)
184 // called by createMetricInstance, below.
186 // a valid metricDefinitionNode* when successful
187 // -1 --> enable was false
188 // -2 --> not legal to instrument this focus
189 // NULL --> a more serious error (probably metric-is-unknown)
192 metricDefinitionNode *mn = 0;
194 // check to see if this is an internal metric
195 unsigned im_size = internalMetric::allInternalMetrics.size();
196 for (unsigned im_index=0; im_index<im_size; im_index++){
197 internalMetric *theIMetric = internalMetric::allInternalMetrics[im_index];
198 if (theIMetric->name() == metric_name) {
201 return (metricDefinitionNode*)-1;
203 if (!theIMetric->legalToInst(canon_focus))
204 // Paradyn will handle this case and report appropriate error msg
205 return (metricDefinitionNode*)-2;
207 mn = new metricDefinitionNode(NULL, metric_name, canon_focus,
208 component_canon_focus,
209 flat_name, theIMetric->aggregate());
212 theIMetric->enableNewInstance(mn);
217 // check to see if this is a cost metric
218 for (unsigned i=0; i < costMetric::allCostMetrics.size(); i++){
219 if(costMetric::allCostMetrics[i]->name() == metric_name){
221 if (!enable) return (metricDefinitionNode*)-1;
222 costMetric *nc = costMetric::allCostMetrics[i];
223 if (!nc->legalToInst(canon_focus)) return (metricDefinitionNode*)-2;
225 mn = new metricDefinitionNode(NULL, metric_name, canon_focus,
226 component_canon_focus,
227 flat_name, nc->aggregate());
236 // No matches found among internal or cost metrics
240 // the following should probably be made a public static member fn of class metric
241 string metricAndCanonFocus2FlatName(const string &metricName,
242 const vector< vector<string> > &canonFocus) {
243 string result = metricName;
245 for (unsigned hierarchy=0; hierarchy < canonFocus.size(); hierarchy++)
246 for (unsigned component=0; component < canonFocus[hierarchy].size();
248 result += canonFocus[hierarchy][component];
253 // the following should probably be made a public static member fn of class metric
254 static bool focus2CanonicalFocus(const vector<unsigned> &focus,
255 vector< vector<string> > &canonFocus,
257 // takes in "focus", writes to "canonFocus". Returns true iff successful.
258 // if "important" is false, don't print error msg on failure (called by guessCost();
259 // no mi is really being created)
261 vector< vector<string> > unCanonFocus;
262 if (!resource::foc_to_strings(unCanonFocus, focus, important)) { // writes to unCanonFocus
264 cerr << "focus2CanonicalFocus failed since resource::foc_to_strings failed" << endl;
268 resource::make_canonical(unCanonFocus, canonFocus);
273 static void print_focus(debug_ostream &os, vector< vector<string> > &focus) {
274 for (unsigned a=0; a < focus.size(); a++) {
275 for (unsigned b=0; b < focus[a].size(); b++)
276 os << '/' << focus[a][b];
278 if (a < focus.size()-1)
284 metricDefinitionNode *createMetricInstance(string& metric_name,
285 vector<u_int>& focus,
286 bool enable, // true if for real; false for guessCost()
289 vector< vector<string> > canonicalFocus;
290 // we make third parameter false to avoid printing warning messages in
291 // focus2CanonicalFocus ("enable" was here previously) - naim
292 if (!focus2CanonicalFocus(focus, canonicalFocus, false)) {
293 //if (enable) cerr << "createMetricInstance failed because focus2CanonicalFocus failed" << endl;
297 //cerr << "createMetricInstance called. metric_name = " << \
298 // metric_name << endl;
299 //cerr << " canonicalFocus (derived from focus (u_int id array)) == " \
301 //for(unsigned z = 0; z < canonicalFocus.size(); z++) {
302 // vector<string> temp_strings = canonicalFocus[z];
303 // cerr << " canonicalFocus[" << z << "] : " << endl;
304 // for(unsigned y = 0; y < temp_strings.size(); y++) {
305 // cerr << " " << temp_strings[y] << endl;
309 string flat_name = metricAndCanonFocus2FlatName(metric_name, canonicalFocus);
310 //cerr << "flat_name = " << flat_name << endl;
313 // first see if it is already defined.
314 dictionary_hash_iter<unsigned, metricDefinitionNode*> mdi(allMIs);
317 * See if we can find the requested metric instance.
318 * Currently this is only used to cache structs built for cost requests
319 * which are then instantiated. This could be used as a general system
320 * to request find sub-metrics that are already.defines and use them to
321 * reduce cost. This would require adding the componenets of an aggregate
322 * into the allMIs list since searching tends to be top down, not bottom
323 * up. This would also require adding a ref count to many of the structures
324 * so they only get deleted when we are really done with them.
328 // first see if it is already defined.
329 for (dictionary_hash_iter<unsigned, metricDefinitionNode*> mdi = allMIs; mdi; mdi++) {
330 metricDefinitionNode *mi = mdi.currval();
332 if (mi->getFullName() == flat_name) {
333 metric_cerr << "createMetricInstance: mi with flat_name " << flat_name << " already exists! using it" << endl;
334 return mi; // this metricDefinitionNode has already been defined
338 //cerr << " previous instance of metric not found, trying to create" \
341 if (mdl_can_do(metric_name)) {
342 //cerr << " mdl_can_do(metrix_name) == TRUE" << endl;
345 /* select the processes that should be instrumented. We skip process
346 that have exited, and processes that have been created but are not
347 completely initialized yet.
348 If we try to insert instrumentation in a process that is not ready
349 yet, we get a core dump.
350 A process is ready when it is not in neonatal state and the
351 isBootstrappedYet returns true.
353 vector<process*> procs;
355 for (unsigned u = 0; u < processVec.size(); u++) {
356 if (processVec[u]->status()==exited || processVec[u]->status()==neonatal
357 || processVec[u]->isBootstrappedYet())
358 procs += processVec[u];
361 if (procs.size() == 0) {
362 // there are no processes to instrument
363 //printf("createMetricInstance failed, no processes to instrument\n");
368 if (enable) computingCost = false;
369 else computingCost = true;
370 metricDefinitionNode *mi = mdl_do(canonicalFocus, metric_name, flat_name, procs, false,
373 //cerr << " mdl_do returned ";
375 // cerr << "NULL" << endl;
377 // cerr << "Non-NULL" << endl;
381 metric_cerr << "createMetricInstance failed since mdl_do failed" << endl;
382 metric_cerr << "metric name was " << metric_name << "; focus was ";
383 print_focus(metric_cerr, canonicalFocus);
387 //cerr << " mdl_can_do(metrix_name) == FALSE" << endl;
389 metricDefinitionNode *mi=doInternalMetric(canonicalFocus,
390 canonicalFocus, // is this right for component_canon_focus???
391 metric_name,flat_name,enable,matched);
392 // NULL on serious error; -1 if enable was false; -2 if illegal to instr with
393 // given focus [many internal metrics work only for whole program]
395 if (mi == (metricDefinitionNode*)-2) {
396 metric_cerr << "createMetricInstance: internal metric " << metric_name << " isn't defined for focus: ";
397 print_focus(metric_cerr, canonicalFocus);
398 mi = NULL; // straighten up the return value
400 else if (mi == (metricDefinitionNode*)-1) {
401 assert(!enable); // no error msg needed
402 mi = NULL; // straighten up the return value
404 else if (mi == NULL) {
405 // more serious error...do a printout
406 metric_cerr << "createMetricInstance failed since doInternalMetric failed" << endl;
407 metric_cerr << "metric name was " << metric_name << "; focus was ";
408 print_focus(metric_cerr, canonicalFocus);
417 // propagate this metric instance to process p.
418 // p is a process that started after the metric instance was created
419 // note: don't call this routine for a process started via fork or exec, just
420 // for processes started the "normal" way.
421 // "this" is an aggregate mi, not a component one.
423 void metricDefinitionNode::propagateToNewProcess(process *p) {
424 unsigned comp_size = components.size();
427 return; // if there are no components, shouldn't the mi be fried?
429 for (unsigned u = 0; u < comp_size; u++) {
430 if (components[u]->proc() == p) {
431 // The metric is already enabled for this process. This case can
432 // happen when we are starting several processes at the same time.
438 bool internal = false;
440 metricDefinitionNode *mi = NULL;
441 // an aggregate (not component) mi, though we know that it'll contain just
442 // one component. It's that one component that we're really interested in.
443 if (mdl_can_do(met_)) {
444 // Make the unique ID for this metric/focus visible in MDL.
445 string vname = "$globalId";
446 mdl_env::add(vname, false, MDL_T_INT);
447 mdl_env::set(this->getMId(), vname);
449 vector<process *> vp(1,p);
450 mi = mdl_do(focus_, met_, flat_name_, vp, false, false);
452 // internal and cost metrics don't need to be propagated (um, is this correct?)
456 if (mi) { // successfully created new mi
457 assert(mi->components.size() == 1);
458 metricDefinitionNode *theNewComponent = mi->components[0];
460 components += theNewComponent;
461 theNewComponent->aggregators[0] = this;
462 theNewComponent->samples[0] = aggSample.newComponent();
464 theNewComponent->insertInstrumentation();
465 theNewComponent->checkAndInstallInstrumentation();
469 const float cost = mi->cost();
470 if (cost > originalCost_) {
471 currentPredictedCost += cost - originalCost_;
472 originalCost_ = cost;
475 mi->components.resize(0); // protect the new component
480 metricDefinitionNode* metricDefinitionNode::handleExec() {
481 // called by handleExec(), below. See that routine for documentation.
482 // "this" is a component mi.
484 // If this component mi can be (re-)enabled in the new (post-exec) process, then do
485 // so. Else, remove the component mi from aggregators, etc. Returns new component
486 // mi if successful, NULL otherwise.
490 // How can we tell if the mi can be inserted into the "new" (post-exec) process?
491 // A component mi is basically a set of instReqNodes and dataReqNodes. The latter
492 // don't restrict what can be inserted (is this right?); the instReqNodes hold the
493 // key -- we should look at the functions (instPoint's) where code (whose contents
494 // are in AstNode's) would be inserted. Now clearly, the instPoint's must be
495 // checked -- if any one doesn't exist, then the instReqNode and hence the component
496 // mi doesn't belong in the post-exec process. But what about the AstNode's?
497 // Should the code that gets inserted be subject to a similar test? Probably, but
498 // we currently don't do it.
500 // BUT: Even if a process contains a function in both the pre-exec and post-exec
501 // stages, we must assume that the function is IN A DIFFERENT LOCATION IN
502 // THE ADDRESS SPACE. Ick. So the instPoint's can't be trusted and must
503 // be recalculated from scratch. In that regard, this routine is similar to
504 // propagateToNewProcess(), which propagates aggregate mi's to a brand new
505 // process (but which doesn't work for processes started via fork or exec).
506 // The lesson learned is to (ick, ick, ick) call mdl_do() all over again.
507 // This gets really confusing when you consider that a component mi can belong
508 // to several aggregate mi's (e.g. if we represent cpu time for proc 100 then
509 // we can belong to cpu/whole and cpu/proc-100); for which aggregate mi should
510 // we run mdl_do? Any will do, so we can pick arbitrarily (is this right?).
512 // QUESTION: What about internal or cost metrics??? They have aggregate and
513 // component mi's just like normal metrics, right? If that's so, then
514 // they must be propagated too! NOT YET IMPLEMENTED!!!
516 metricDefinitionNode *aggregateMI = this->aggregators[0];
517 metricDefinitionNode *resultCompMI = NULL; // so far...
519 const bool internal = !mdl_can_do(aggregateMI->met_);
521 return NULL; // NOT YET IMPLEMENTED
523 // try to propagate the mi
524 // note: the following code is mostly stolen from propagateToNewProcess(); blame
525 // it for any bugs :)
527 // Make the unique ID for this metric/focus visible in MDL. (?)
528 string vname = "$globalId";
529 mdl_env::add(vname, false, MDL_T_INT);
530 mdl_env::set(aggregateMI->getMId(), vname);
532 vector<process*> vp(1, this->proc());
533 metricDefinitionNode *tempAggMI = mdl_do(aggregateMI->focus_,
535 aggregateMI->flat_name_,
537 true, // fry existing component MI
539 if (tempAggMI == NULL)
540 return NULL; // failure
542 assert(tempAggMI->aggregate_);
544 // okay, it looks like we successfully created a new aggregate mi.
545 // Of course, we're just interested in the (single) component mi contained
546 // within it; it'll replace "this".
548 assert(tempAggMI->components.size() == 1);
549 resultCompMI = tempAggMI->components[0];
551 resultCompMI->aggregators.resize(0);
552 resultCompMI->samples.resize(0);
554 // For each aggregator, go back and find where "this" was a component mi.
555 // When found, replace the ptr to "this" with "theNewComponent".
556 unsigned num_aggregators = aggregators.size();
557 assert(num_aggregators > 0);
558 for (unsigned agglcv=0; agglcv < num_aggregators; agglcv++) {
559 metricDefinitionNode *aggMI = aggregators[agglcv];
562 for (unsigned complcv=0; complcv < aggMI->components.size(); complcv++) {
563 if (aggMI->components[complcv] == this) {
564 aggMI->components[complcv] = resultCompMI;
566 resultCompMI->aggregators += aggMI;
567 resultCompMI->samples += aggMI->aggSample.newComponent();
569 aggMI->aggSample.removeComponent(this->samples[agglcv]);
578 // Now let's actually insert the instrumentation:
580 resultCompMI->insertInstrumentation();
581 resultCompMI->checkAndInstallInstrumentation();
584 // And fry "tempAggMI", but make sure "resultCompMI" isn't fried when we do so
585 tempAggMI->components.resize(0); // protect resultCompMI
586 delete tempAggMI; // good riddance; you were an ugly hack to begin with
591 void metricDefinitionNode::handleExec(process *proc) {
592 // a static member fn.
593 // handling exec is tricky. At the time this routine is called, the "new" process
594 // has been bootstrapped and is ready for stuff to get inserted. No mi's have yet
595 // been propagated, and the data structures (allMIs, allMIComponents, etc.) are still
596 // in their old, pre-exec state, so they show component mi's enabled for this
597 // process, even though they're not (at least not yet). This routines brings things
600 // Algorithm: loop thru all component mi's for this process. If it is possible to
601 // propagate it to the "new" (post-exec) process, then do so. If not, fry the
602 // component mi. An example where a component mi can no longer fit is an mi
603 // specific to, say, function foo(), which (thanks to the exec syscall) no longer
604 // exists in this process. Note that the exec syscall changed the addr space enough
605 // so even if a given routine foo() is present in both the pre-exec and post-exec
606 // process, we must assume that it has MOVED TO A NEW LOCATION, thus making
607 // the component mi's instReqNode's instPoint out-of-date. Ick.
609 // note the two loops; we can't safely combine into one since the second loop modifies
611 vector<metricDefinitionNode*> allcomps;
612 for (dictionary_hash_iter<string,metricDefinitionNode*> iter=allMIComponents; iter; iter++)
613 allcomps += iter.currval();
615 for (unsigned i=0; i < allcomps.size(); i++) {
616 metricDefinitionNode* componentMI = allcomps[i];
617 if (componentMI->proc() != proc)
620 forkexec_cerr << "calling handleExec for component "
621 << componentMI->flat_name_ << endl;
623 metricDefinitionNode *replaceWithComponentMI = componentMI->handleExec();
625 if (replaceWithComponentMI == NULL) {
626 forkexec_cerr << "handleExec for component " << componentMI->flat_name_
627 << " failed, so not propagating it" << endl;
628 componentMI->removeThisInstance(); // propagation failed; fry component mi
631 forkexec_cerr << "handleExec for component " << componentMI->flat_name_
632 << " succeeded...it has been propagated" << endl;
633 // new component mi has already been inserted in place of old component mi
634 // in all of its aggregate's component lists. So, not much left to do,
635 // except to update allMIComponents.
637 assert(replaceWithComponentMI->flat_name_ == componentMI->flat_name_);
639 delete componentMI; // old component mi (dtor removes it from allMIComponents)
640 assert(!allMIComponents.defines(replaceWithComponentMI->flat_name_));
641 allMIComponents[replaceWithComponentMI->flat_name_] = replaceWithComponentMI;
646 // called when all components have been removed (because the processes have exited
647 // or exec'd) from "this". "this" is an aggregate mi.
648 void metricDefinitionNode::endOfDataCollection() {
649 assert(components.size() == 0);
651 // flush aggregateSamples
652 sampleInterval ret = aggSample.aggregateValues();
655 assert(ret.end > ret.start);
656 assert(ret.start >= (firstRecordTime/MILLION));
657 assert(ret.end >= (firstRecordTime/MILLION));
658 batchSampleData(id_, ret.start, ret.end, ret.value,
659 aggSample.numComponents(),false);
660 ret = aggSample.aggregateValues();
662 flush_batch_buffer();
663 // trace data streams
664 extern dictionary_hash<unsigned, unsigned> traceOn;
665 for (dictionary_hash_iter<unsigned,unsigned> iter=traceOn; iter; iter++) {
666 unsigned key = iter.currkey();
667 unsigned val = iter.currval();
670 extern void batchTraceData(int, int, int, char *);
671 extern bool TRACE_BURST_HAS_COMPLETED;
672 TRACE_BURST_HAS_COMPLETED = true;
673 batchTraceData(0, key, 0, (char *)NULL);
677 tp->endOfDataCollection(id_);
680 // remove a component from an aggregate.
681 // "this" is an aggregate mi; "comp" is a component mi.
682 void metricDefinitionNode::removeFromAggregate(metricDefinitionNode *comp,
684 unsigned size = components.size();
685 for (unsigned u = 0; u < size; u++) {
686 if (components[u] == comp) {
687 if (deleteComp) delete components[u];
688 components[u] = NULL;
689 components[u] = components[size-1];
690 components.resize(size-1);
692 endOfDataCollection();
697 // should always find the right component
701 // remove this component mi from all aggregators it is a component of.
702 // if the aggregate mi no longer has any components then fry the mi aggregate mi.
703 // called by removeFromMetricInstances, below, when a process exits (or exec's)
704 void metricDefinitionNode::removeThisInstance() {
707 // first, remove from allMIComponents (this is new --- is it right?)
708 if (allMIComponents.defines(flat_name_)) {
709 allMIComponents.undef(flat_name_);
712 assert(aggregators.size() == samples.size());
714 for (unsigned u = 0; u < aggregators.size() && u < samples.size(); u++) {
715 aggregators[u]->aggSample.removeComponent(samples[u]);
716 aggregators[u]->removeFromAggregate(this, 0);
721 // Called when a process exits, to remove the component associated to proc
722 // from all metric instances. (If, after an exec, we never want to carry over
723 // mi's from the pre-exec, then this routine will work there, too. But we try to
724 // carry over mi's whenever appropriate.)
725 // Remove the aggregate metric instances that don't have any components left
726 void removeFromMetricInstances(process *proc) {
727 // Loop through all of the _component_ mi's; for each with component process
728 // of "proc", remove the component mi from its aggregate mi.
729 // Note: imho, there should be a *per-process* vector of mi-components.
731 // note 2 loops for safety (2d loop may modify dictionary?)
732 vector<metricDefinitionNode *> MIs;
733 for (dictionary_hash_iter<string,metricDefinitionNode*> iter=allMIComponents; iter; iter++)
734 MIs += iter.currval();
736 for (unsigned j = 0; j < MIs.size(); j++) {
737 if (MIs[j]->proc() == proc)
738 MIs[j]->removeThisInstance();
740 costMetric::removeProcessFromAll(proc); // what about internal metrics?
743 /* *************************************************************************** */
745 // obligatory definition of static member vrble:
746 int metricDefinitionNode::counterId=0;
748 dataReqNode *metricDefinitionNode::addSampledIntCounter(int initialValue,
752 dataReqNode *result=NULL;
755 // shared memory sampling of a reported intCounter
756 result = new sampledShmIntCounterReqNode(initialValue,
757 metricDefinitionNode::counterId,
758 this, computingCost, doNotSample);
759 // implicit conversion to base class
761 // non-shared-memory sampling of a reported intCounter
762 result = new sampledIntCounterReqNode(initialValue,
763 metricDefinitionNode::counterId,
764 this, computingCost);
765 // implicit conversion to base class
770 metricDefinitionNode::counterId++;
772 internalMetricCounterId = metricDefinitionNode::counterId;
774 dataRequests += result;
778 dataReqNode *metricDefinitionNode::addUnSampledIntCounter(int initialValue,
779 bool computingCost) {
780 // sampling of a non-reported intCounter (probably just a predicate)
781 // NOTE: In the future, we should probably put un-sampled intcounters
782 // into shared-memory when SHM_SAMPLING is defined. After all, the shared
783 // memory heap is faster.
784 dataReqNode *result = new nonSampledIntCounterReqNode
785 (initialValue, metricDefinitionNode::counterId,
786 this, computingCost);
787 // implicit conversion to base class
790 metricDefinitionNode::counterId++;
792 internalMetricCounterId = metricDefinitionNode::counterId;
794 dataRequests += result;
798 dataReqNode *metricDefinitionNode::addWallTimer(bool computingCost) {
799 dataReqNode *result = NULL;
802 result = new sampledShmWallTimerReqNode(metricDefinitionNode::counterId, this, computingCost);
803 // implicit conversion to base class
805 result = new sampledTimerReqNode(wallTime, metricDefinitionNode::counterId, this, computingCost);
806 // implicit conversion to base class
811 metricDefinitionNode::counterId++;
813 internalMetricCounterId = metricDefinitionNode::counterId;
815 dataRequests += result;
819 dataReqNode *metricDefinitionNode::addProcessTimer(bool computingCost) {
820 dataReqNode *result = NULL;
823 result = new sampledShmProcTimerReqNode(metricDefinitionNode::counterId, this, computingCost);
824 // implicit conversion to base class
826 result = new sampledTimerReqNode(processTime, metricDefinitionNode::counterId, this, computingCost);
827 // implicit conversion to base class
832 metricDefinitionNode::counterId++;
834 internalMetricCounterId = metricDefinitionNode::counterId;
836 dataRequests += result;
840 /* *************************************************************************** */
842 // called when a process forks (by handleFork(), below). "this" is a (component)
843 // mi in the parent process. Duplicate it for the child, with appropriate
844 // changes (i.e. the pid of the component focus name differs), and return the newly
845 // created child mi. "map" maps all instInstance's of the parent to those copied into
848 // Note how beautifully everything falls into place. Consider the case of alarm
849 // sampling with cpu/whole program. Then comes the fork. The parent process has
850 // (0) a tTimer structure allocated in a specific location in the inferior heap,
851 // (1) instrumentation @ main to call startTimer on that ptr, (2) instrumentation in
852 // DYNINSTsampleValues() to call DYNINSTreportTimer on that ptr.
853 // The child process of fork will have ALL of these things in the exact same locations,
854 // which is correct. We want the timer to be located in the same spot; we want
855 // DYNINSTreportTimer to be called on the same pointer; and main() hasn't moved.
857 // So there's not much to do here. We create a new component mi (with same flat name
858 // as in the parent, except for a different pid), and call "forkProcess" for all
859 // dataReqNodes and instReqNodes, none of which have to do anything titanic.
861 metricDefinitionNode *metricDefinitionNode::forkProcess(process *child,
862 const dictionary_hash<instInstance*,instInstance*> &map) const {
863 // The "focus_" member vrble stays the same, because it was always for the
864 // metric as a whole, and not for some component.
866 // But two things must change, because they were component-specific (and the
867 // component has changed processes):
869 // (2) the component focus (not to be confused with plain focus_)
871 // For example, instead of
872 // "/Code/foo.c/myfunc, /Process/100, ...", we should have
873 // "/Code/foo.c/myfunc, /Process/101, ...", because the pid of the child
874 // differs from that of the parent.
876 // The resource structure of a given process is found in the "rid"
877 // field of class process.
878 const resource *parentResource = child->getParent()->rid;
879 const string &parentPartName = parentResource->part_name();
881 const resource *childResource = child->rid;
882 const string &childPartName = childResource->part_name();
884 vector< vector<string> > newComponentFocus = this->component_focus;
885 // we'll change the process, but not the machine name.
886 bool foundProcess = false;
888 for (unsigned hier=0; hier < component_focus.size(); hier++) {
889 if (component_focus[hier][0] == "Process") {
891 assert(component_focus[hier].size() == 2);
892 // since a component focus is by definition specific to some process
894 assert(component_focus[hier][1] == parentPartName);
896 // change the process:
897 newComponentFocus[hier][1] = childPartName;
901 assert(foundProcess);
903 string newComponentFlatName = metricAndCanonFocus2FlatName(met_, newComponentFocus);
905 metricDefinitionNode *mi =
906 new metricDefinitionNode(child,
907 met_, // metric name doesn't change
908 focus_, // focus doesn't change (tho component focus will)
909 newComponentFocus, // this is a change
910 newComponentFlatName, // this is a change
915 metricDefinitionNode::counterId++;
917 forkexec_cerr << "metricDefinitionNode::forkProcess -- component flat name for parent is " << flat_name_ << "; for child is " << mi->flat_name_ << endl;
919 internalMetricCounterId = metricDefinitionNode::counterId;
921 assert(!allMIComponents.defines(newComponentFlatName));
922 allMIComponents[newComponentFlatName] = mi;
924 // Duplicate the dataReqNodes:
925 for (unsigned u1 = 0; u1 < dataRequests.size(); u1++) {
926 // must add to midToMiMap[] before dup() to avoid some assert fails
927 const int newCounterId = metricDefinitionNode::counterId++;
928 // no relation to mi->getMId();
929 forkexec_cerr << "forked dataReqNode going into midToMiMap with id " << newCounterId << endl;
930 assert(!midToMiMap.defines(newCounterId));
931 midToMiMap[newCounterId] = mi;
933 dataReqNode *newNode = dataRequests[u1]->dup(child, mi, newCounterId, map);
934 // remember, dup() is a virtual fn, so the right dup() and hence the
935 // right fork-ctor is called.
938 mi->dataRequests += newNode;
941 // Duplicate the instReqNodes:
942 for (unsigned u2 = 0; u2 < instRequests.size(); u2++) {
943 mi->instRequests += instReqNode::forkProcess(instRequests[u2], map);
946 mi->inserted_ = true;
951 bool metricDefinitionNode::unFork(dictionary_hash<instInstance*, instInstance*> &map,
952 bool unForkInstRequests,
953 bool unForkDataRequests) {
954 // see below handleFork() for explanation of why this routine is needed.
955 // "this" is a component mi for the parent process; we need to remove copied
956 // instrumentation from the _child_ process.
957 // Returns true iff the instrumentation was removed in the child (would be false
958 // if it's not safe to remove the instrumentation in the child because it was
961 // "map" maps instInstances from the parent process to instInstances in the child
964 // We loop thru the instReqNodes of the parent process, unforking each.
965 // In addition, we need to unfork the dataReqNodes, because the alarm-sampled
966 // ones instrument DYNINSTsampleValues.
970 if (unForkInstRequests)
971 for (unsigned lcv=0; lcv < instRequests.size(); lcv++)
972 if (!instRequests[lcv].unFork(map))
973 result = false; // failure
975 if (unForkDataRequests)
976 for (unsigned lcv=0; lcv < dataRequests.size(); lcv++)
977 if (!dataRequests[lcv]->unFork(map))
978 result = false; // failure
984 // called by forkProcess of context.C, just after the fork-constructor was
985 // called for the child process.
986 void metricDefinitionNode::handleFork(const process *parent, process *child,
987 dictionary_hash<instInstance*,instInstance*> &map) {
988 // "map" defines a mapping from all instInstance's of the parent process to
989 // the copied one in the child process. Some of the child process's ones may
990 // get fried by this routine, as it detects that instrumentation has been copied
991 // (by the fork syscall, which we have no control over) which doesn't belong in
992 // the child process and therefore gets deleted manually.
994 // Remember that a given component can be shared by multiple aggregator-mi's,
995 // so be careful about duplicating a component twice. Since we loop through
996 // component mi's instead of aggregate mi's, it's no problem. Note that it's
997 // possible that only a subset of a component-mi's aggregators should get the newly
998 // created child component mi.
1000 // 2 loops for safety (2d loop may modify dictionary?)
1001 vector<metricDefinitionNode *> allComponents;
1002 for (dictionary_hash_iter<string,metricDefinitionNode*> iter=allMIComponents; iter; iter++)
1003 allComponents += iter.currval();
1005 for (unsigned complcv=0; complcv < allComponents.size(); complcv++) {
1006 metricDefinitionNode *comp = allComponents[complcv];
1008 // duplicate the component (create a new one) if it belongs in the
1009 // child process. It belongs if any of its aggregate mi's should be
1010 // propagated to the child process. An aggregate mi should be propagated
1011 // if it wasn't refined to some process.
1013 bool shouldBePropagated = false; // so far
1014 bool shouldBeUnforkedIfNotPropagated = false; // so far
1015 assert(comp->aggregators.size() > 0);
1016 for (unsigned agglcv1=0; agglcv1 < comp->aggregators.size(); agglcv1++) {
1017 metricDefinitionNode *aggMI = comp->aggregators[agglcv1];
1019 if (aggMI->focus_[resource::process].size() == 1) {
1020 // wasn't specific to any process
1021 shouldBeUnforkedIfNotPropagated = false; // we'll definitely be using it
1022 shouldBePropagated = true;
1025 else if (comp->proc() == parent)
1026 // was specific to parent process, so fork() copied it into the child,
1027 // unless it was an internal or cost metric, in which case there was nothing
1028 // for fork to copy.
1029 if (!internalMetric::isInternalMetric(aggMI->getMetName()) &&
1030 !costMetric::isCostMetric(aggMI->getMetName()))
1031 shouldBeUnforkedIfNotPropagated = true;
1033 // was specific to other process, so nothing is in the child for it yet
1037 if (!shouldBePropagated && shouldBeUnforkedIfNotPropagated) {
1038 // this component mi isn't gonna be propagated to the child process, but
1039 // the fork syscall left some residue in the child. Delete that residue now.
1040 assert(comp->proc() == parent);
1041 comp->unFork(map, true, true); // also modifies 'map' to remove items
1044 if (!shouldBePropagated)
1047 // Okay, it's time to propagate this component mi to the subset of its aggregate
1048 // mi's which weren't refined to a specific process. If we've gotten to this
1049 // point, then there _is_ at least one such aggregate.
1050 assert(shouldBePropagated);
1051 metricDefinitionNode *newComp = comp->forkProcess(child, map);
1052 // copies instr (well, fork() does this for us), allocs ctr/timer space,
1053 // initializes. Basically, copies dataReqNode's and instReqNode's.
1055 bool foundAgg = false;
1056 for (unsigned agglcv2=0; agglcv2 < comp->aggregators.size(); agglcv2++) {
1057 metricDefinitionNode *aggMI = comp->aggregators[agglcv2];
1058 if (aggMI->focus_[resource::process].size() == 1) {
1059 // this aggregate mi wasn't specific to any process, so it gets the new
1061 aggMI->components += newComp;
1062 newComp->aggregators += aggMI;
1063 newComp->samples += aggMI->aggSample.newComponent();
1071 bool metricDefinitionNode::anythingToManuallyTrigger() const {
1073 for (unsigned i=0; i < components.size(); i++)
1074 if (components[i]->anythingToManuallyTrigger())
1079 for (unsigned i=0; i < instRequests.size(); i++)
1080 if (instRequests[i].anythingToManuallyTrigger())
1088 void metricDefinitionNode::manuallyTrigger(int parentMId) {
1089 assert(anythingToManuallyTrigger());
1092 for (unsigned i=0; i < components.size(); i++)
1093 if (components[i]->anythingToManuallyTrigger())
1094 components[i]->manuallyTrigger(parentMId);
1097 for (unsigned i=0; i < instRequests.size(); i++)
1098 if (instRequests[i].anythingToManuallyTrigger()) {
1099 if (!instRequests[i].triggerNow(proc(),parentMId)) {
1100 cerr << "manual trigger failed for an inst request" << endl;
1107 // startCollecting is called by dynRPC::enableDataCollection (or enableDataCollection2)
1109 // startCollecting is a friend of metricDefinitionNode; can it be
1110 // made a member function of metricDefinitionNode instead?
1111 // Especially since it clearly is an integral part of the class;
1112 // in particular, it sets the crucial vrble "id_"
1113 int startCollecting(string& metric_name, vector<u_int>& focus, int id,
1114 vector<process *> &procsToCont)
1116 bool internal = false;
1118 // Make the unique ID for this metric/focus visible in MDL.
1119 string vname = "$globalId";
1120 mdl_env::add(vname, false, MDL_T_INT);
1121 mdl_env::set(id, vname);
1123 metricDefinitionNode *mi = createMetricInstance(metric_name, focus,
1127 //cerr << "startCollecting for " << metric_name << " failed because createMetricInstance failed" << endl;
1133 assert(!allMIs.defines(mi->id_));
1134 allMIs[mi->id_] = mi;
1136 const float cost = mi->cost();
1137 mi->originalCost_ = cost;
1139 currentPredictedCost += cost;
1142 // enable timing stuff: also code in insertInstrumentation()
1143 u_int start_size = test_heapsize;
1144 printf("ENABLE: %d %s %s\n",start_size,
1145 (mi->getMetName()).string_of(),
1146 (mi->getFullName()).string_of());
1147 static timer inTimer;
1154 // pause processes that are running and add them to procsToCont.
1155 // We don't rerun the processes after we insert instrumentation,
1156 // this will be done by our caller, after all instrumentation
1157 // has been inserted.
1158 for (unsigned u = 0; u < mi->components.size(); u++) {
1159 process *p = mi->components[u]->proc();
1160 if (p->status() == running && p->pause()) {
1166 mi->insertInstrumentation(); // calls pause and unpause (this could be a bug, since the next line should be allowed to execute before the unpause!!!)
1167 mi->checkAndInstallInstrumentation();
1169 // Now that the timers and counters have been allocated on the heap, and
1170 // the instrumentation added, we can manually execute instrumentation
1171 // we may have missed at $start.entry. But has the process been paused
1172 // all this time? Hopefully so; otherwise things can get screwy.
1174 if (mi->anythingToManuallyTrigger()) {
1175 process *theProc = mi->components[0]->proc();
1178 bool alreadyRunning = (theProc->status_ == running);
1183 mi->manuallyTrigger(id);
1186 theProc->continueProc(); // the continue will trigger our code
1188 ; // the next time the process continues, we'll trigger our code
1194 if(!start_size) start_size = test_heapsize;
1195 printf("It took %f:user %f:system %f:wall seconds heap_left: %d used %d\n"
1196 , inTimer.usecs(), inTimer.ssecs(), inTimer.wsecs(),
1197 test_heapsize,start_size-test_heapsize);
1200 metResPairsEnabled++;
1204 float guessCost(string& metric_name, vector<u_int>& focus) {
1205 // called by dynrpc.C (getPredictedDataCost())
1207 metricDefinitionNode *mi = createMetricInstance(metric_name, focus, false, internal);
1209 //metric_cerr << "guessCost returning 0.0 since createMetricInstance failed" << endl;
1213 float cost = mi->cost();
1214 // delete the metric instance, if it is not being used
1215 if (!allMIs.defines(mi->getMId()))
1221 bool metricDefinitionNode::insertInstrumentation()
1223 // returns true iff successful
1230 unsigned c_size = components.size();
1231 for (unsigned u=0; u<c_size; u++)
1232 if (!components[u]->insertInstrumentation())
1233 return false; // shouldn't we try to undo what's already put in?
1235 bool needToCont = proc_->status() == running;
1236 bool res = proc_->pause();
1240 // Loop thru "dataRequests", an array of (ptrs to) dataReqNode:
1241 // Here we allocate ctrs/timers in the inferior heap but don't
1242 // stick in any code, except (if appropriate) that we'll instrument the
1243 // application's alarm-handler when not shm sampling.
1244 unsigned size = dataRequests.size();
1245 for (unsigned u=0; u<size; u++) {
1246 // the following allocs an object in inferior heap and arranges for
1247 // it to be alarm sampled, if appropriate.
1248 // Note: this is not necessary anymore because we are allocating the
1249 // space when the constructor for dataReqNode is called. This was
1250 // done for the dyninstAPI - naim 2/18/97
1251 //if (!dataRequests[u]->insertInstrumentation(proc_, this))
1252 // return false; // shouldn't we try to undo what's already put in?
1254 unsigned mid = dataRequests[u]->getSampleId();
1255 assert(!midToMiMap.defines(mid));
1256 midToMiMap[mid] = this;
1259 // Loop thru "instRequests", an array of instReqNode:
1260 // (Here we insert code instrumentation, tramps, etc. via addInstFunc())
1261 for (unsigned u1=0; u1<instRequests.size(); u1++) {
1262 // NEW: the following may also manually trigger the instrumentation
1264 returnInstance *retInst=NULL;
1265 if (!instRequests[u1].insertInstrumentation(proc_, retInst))
1266 return false; // shouldn't we try to undo what's already put in?
1269 returnInsts += retInst;
1273 proc_->continueProc();
1279 bool metricDefinitionNode::checkAndInstallInstrumentation() {
1280 // Patch up the application to make it jump to the base trampoline(s) of this
1281 // metric. (The base trampoline and mini-tramps have already been installed
1282 // in the inferior heap). We must first check to see if it's safe to install by
1283 // doing a stack walk, and determining if anything on it overlaps with any of our
1284 // desired jumps to base tramps.
1285 // The key variable is "returnsInsts", which was created for us when the base
1286 // tramp(s) were created. Essentially, it contains the details of how we'll jump
1287 // to the base tramp (where in the code to patch, how many instructions, the
1288 // instructions themselves).
1289 // Note that it seems this routine is misnamed: it's not instrumentation that needs
1290 // to be installed (the base & mini tramps are already in place); it's just the
1291 // last step that is still needed: the jump to the base tramp.
1292 // If one or more can't be added, then a TRAP insn is inserted in the closest
1293 // common safe return point along the stack walk, and some structures are appended
1294 // to the process' "wait list", which is then checked when a TRAP signal arrives.
1295 // At that time, the jump to the base tramp is finally done. WARNING: It seems to
1296 // me that such code isn't thread-safe...just because one thread hits the TRAP,
1297 // there may still be other threads that are unsafe. It seems to me that we should
1298 // be doing this check again when a TRAP arrives...but for each thread (right now,
1299 // there's no stack walk for other threads). --ari
1301 bool needToCont = false;
1303 if (installed_) return(true);
1308 unsigned c_size = components.size();
1309 for (unsigned u=0; u<c_size; u++)
1310 components[u]->checkAndInstallInstrumentation();
1311 // why no checking of the return value?
1313 needToCont = proc_->status() == running;
1314 if (!proc_->pause()) {
1315 cerr << "checkAnd... pause failed" << endl; cerr.flush();
1319 vector<Address> pc = proc_->walkStack();
1320 // ndx 0 is where the pc is now; ndx 1 is the call site;
1321 // ndx 2 is the call site's call site, etc...
1323 // for(u_int i=0; i < pc.size(); i++){
1324 // printf("frame %d: pc = 0x%x\n",i,pc[i]);
1327 unsigned rsize = returnInsts.size();
1328 u_int max_index = 0; // first frame where it is safe to install instr
1329 bool delay_install = false; // true if some instr. needs to be delayed
1330 vector<bool> delay_elm(rsize); // wch instr. to delay
1331 // for each inst point walk the stack to determine if it can be
1332 // inserted now (it can if it is not currently on the stack)
1333 // If some can not be inserted, then find the first safe point on
1334 // the stack where all can be inserted, and set a break point
1335 for (unsigned u=0; u<rsize; u++) {
1337 bool installSafe = returnInsts[u] -> checkReturnInstance(pc,index);
1338 // if unsafe, index will be set to the first unsafe stack walk ndx
1339 // (0 being top of stack; i.e. the current pc)
1341 if (!installSafe && index > max_index)
1345 returnInsts[u] -> installReturnInstance(proc_);
1346 delay_elm[u] = false;
1348 delay_install = true;
1349 delay_elm[u] = true;
1353 if (delay_install) {
1354 // get rid of pathological cases...caused by threaded applications
1355 // TODO: this should be fixed to do something smarter
1356 if(max_index > 0 && max_index+1 >= pc.size()){
1358 //printf("max_index changed: %d\n",max_index);
1360 if(max_index > 0 && pc[max_index+1] == 0){
1362 //printf("max_index changed: %d\n",max_index);
1364 Address pc2 = pc[max_index+1];
1365 for (u_int i=0; i < rsize; i++)
1367 returnInsts[i]->addToReturnWaitingList(pc2, proc_);
1370 if (needToCont) proc_->continueProc();
1375 float metricDefinitionNode::cost() const
1379 unsigned c_size = components.size();
1380 for (unsigned u=0; u<c_size; u++) {
1381 float nc = components[u]->cost();
1382 if (nc > ret) ret = nc;
1385 for (unsigned u=0; u<instRequests.size(); u++)
1386 ret += instRequests[u].cost(proc_);
1391 void metricDefinitionNode::disable()
1393 // check for internal metrics
1395 unsigned ai_size = internalMetric::allInternalMetrics.size();
1396 for (unsigned u=0; u<ai_size; u++) {
1397 internalMetric *theIMetric = internalMetric::allInternalMetrics[u];
1398 if (theIMetric->disableByMetricDefinitionNode(this)) {
1399 //logLine("disabled internal metric\n");
1404 // check for cost metrics
1405 for (unsigned i=0; i<costMetric::allCostMetrics.size(); i++){
1406 if (costMetric::allCostMetrics[i]->node == this) {
1407 costMetric::allCostMetrics[i]->disable();
1408 //logLine("disabled cost metric\n");
1412 if (!inserted_) return;
1416 /* disable components of aggregate metrics */
1417 for (unsigned u=0; u<components.size(); u++) {
1418 metricDefinitionNode *m = components[u];
1419 unsigned aggr_size = m->aggregators.size();
1420 assert(aggr_size == m->samples.size());
1421 for (unsigned u1=0; u1 < aggr_size; u1++) {
1422 if (m->aggregators[u1] == this) {
1423 m->aggregators[u1] = m->aggregators[aggr_size-1];
1424 m->aggregators.resize(aggr_size-1);
1425 m->samples[u1] = m->samples[aggr_size-1];
1426 m->samples.resize(aggr_size-1);
1431 assert(m->aggregators.size() == aggr_size-1);
1433 // disable component only if it is not being shared
1434 if (aggr_size == 1) {
1440 vector<unsigVecType> pointsToCheck;
1441 for (unsigned u1=0; u1<instRequests.size(); u1++) {
1442 unsigVecType pointsForThisRequest =
1443 getAllTrampsAtPoint(instRequests[u1].getInstance());
1444 pointsToCheck += pointsForThisRequest;
1446 instRequests[u1].disable(pointsForThisRequest); // calls deleteInst()
1449 for (unsigned u=0; u<dataRequests.size(); u++) {
1450 unsigned mid = dataRequests[u]->getSampleId();
1451 dataRequests[u]->disable(proc_, pointsToCheck); // deinstrument
1452 assert(midToMiMap.defines(mid));
1453 midToMiMap.undef(mid);
1458 void metricDefinitionNode::removeComponent(metricDefinitionNode *comp) {
1459 assert(!comp->aggregate_);
1460 unsigned aggr_size = comp->aggregators.size();
1461 unsigned found = aggr_size;
1463 if (aggr_size == 0) {
1468 // component has more than one aggregator. Remove this from list of aggregators
1469 for (unsigned u = 0; u < aggr_size; u++) {
1470 if (comp->aggregators[u] == this) {
1475 if (found == aggr_size)
1477 assert(found < aggr_size);
1478 assert(aggr_size == comp->samples.size());
1479 comp->aggregators[found] = comp->aggregators[aggr_size-1];
1480 comp->aggregators.resize(aggr_size-1);
1481 comp->samples[found] = comp->samples[aggr_size-1];
1482 comp->samples.resize(aggr_size-1);
1484 if (aggr_size == 1) {
1491 metricDefinitionNode::~metricDefinitionNode()
1494 /* delete components of aggregate metrics */
1495 unsigned c_size = components.size();
1496 for (unsigned u=0; u<c_size; u++)
1497 removeComponent(components[u]);
1498 //delete components[u];
1499 components.resize(0);
1501 allMIComponents.undef(flat_name_);
1502 for (unsigned u=0; u<dataRequests.size(); u++) {
1503 delete dataRequests[u];
1505 dataRequests.resize(0);
1509 void metricDefinitionNode::cleanup_drn()
1511 // we assume that it is safe to delete a dataReqNode at this point,
1512 // otherwise, we would need to do something similar as in the disable
1513 // method for metricDefinitionNode - naim
1514 vector<unsigVecType> pointsToCheck;
1515 for (unsigned u=0; u<dataRequests.size(); u++) {
1516 dataRequests[u]->disable(proc_, pointsToCheck); // deinstrument
1520 // NOTE: This stuff (flush_batch_buffer() and batchSampleData()) belongs
1521 // in perfStream.C; this is an inappropriate file.
1523 //////////////////////////////////////////////////////////////////////////////
1524 // Buffer the samples before we actually send it //
1525 // Send it when the buffers are full //
1526 // or, send it when the last sample in the interval has arrived. //
1527 //////////////////////////////////////////////////////////////////////////////
1529 const unsigned SAMPLE_BUFFER_SIZE = (1*1024)/sizeof(T_dyninstRPC::batch_buffer_entry);
1530 bool BURST_HAS_COMPLETED = false;
1531 // set to true after a burst (after a processTraceStream(), or sampleNodes for
1532 // the CM5), which will force the buffer to be flushed before it fills up
1533 // (if not, we'd have bad response time)
1535 vector<T_dyninstRPC::batch_buffer_entry> theBatchBuffer (SAMPLE_BUFFER_SIZE);
1536 unsigned int batch_buffer_next=0;
1538 // The following routines (flush_batch_buffer() and batchSampleData() are
1539 // in an inappropriate src file...move somewhere more appropriate)
1540 void flush_batch_buffer() {
1541 // don't need to flush if the batch had no data (this does happen; see
1543 if (batch_buffer_next == 0)
1546 // alloc buffer of the exact size to make communication
1547 // more efficient. Why don't we send theBatchBuffer with a count?
1548 // This would work but would always (in the igen call) copy the entire
1549 // vector. This solution has the downside of calling new but is not too bad
1551 vector<T_dyninstRPC::batch_buffer_entry> copyBatchBuffer(batch_buffer_next);
1552 assert(copyBatchBuffer.size() <= theBatchBuffer.size());
1553 for (unsigned i=0; i< batch_buffer_next; i++) {
1554 copyBatchBuffer[i] = theBatchBuffer[i];
1559 t1=getCurrentTime(false);
1562 // Now let's do the actual igen call!
1563 tp->batchSampleDataCallbackFunc(0, copyBatchBuffer);
1566 t2=getCurrentTime(false);
1567 if ((float)(t2-t1) > 15.0) {
1568 sprintf(errorLine,"++--++ TEST ++--++ batchSampleDataCallbackFunc took %5.2f secs, size=%d, Kbytes=%5.2f\n",(float)(t2-t1),sizeof(T_dyninstRPC::batch_buffer_entry),(float)(sizeof(T_dyninstRPC::batch_buffer_entry)*copyBatchBuffer.size()/1024.0));
1573 BURST_HAS_COMPLETED = false;
1574 batch_buffer_next = 0;
1577 void batchSampleData(int mid, double startTimeStamp,
1578 double endTimeStamp, double value, unsigned val_weight,
1579 bool internal_metric)
1581 // This routine is called where we used to call tp->sampleDataCallbackFunc.
1582 // We buffer things up and eventually call tp->batchSampleDataCallbackFunc
1585 char myLogBuffer[120] ;
1586 sprintf(myLogBuffer, "mid %d, value %g\n", mid, value) ;
1587 logLine(myLogBuffer) ;
1590 // Flush the buffer if (1) it is full, or (2) for good response time, after
1592 if (batch_buffer_next >= SAMPLE_BUFFER_SIZE || BURST_HAS_COMPLETED)
1593 flush_batch_buffer();
1595 // Now let's batch this entry.
1596 T_dyninstRPC::batch_buffer_entry &theEntry = theBatchBuffer[batch_buffer_next];
1598 theEntry.startTimeStamp = startTimeStamp;
1599 theEntry.endTimeStamp = endTimeStamp;
1600 theEntry.value = value;
1601 theEntry.weight = val_weight;
1602 theEntry.internal_met = internal_metric;
1603 batch_buffer_next++;
1606 //////////////////////////////////////////////////////////////////////////////
1607 // Buffer the traces before we actually send it //
1608 // Send it when the buffers are full //
1609 // or, send it when the last sample in the interval has arrived. //
1610 //////////////////////////////////////////////////////////////////////////////
1612 const unsigned TRACE_BUFFER_SIZE = 10;
1613 bool TRACE_BURST_HAS_COMPLETED = false;
1614 // set to true after a burst (after a processTraceStream(), or sampleNodes for
1615 // the CM5), which will force the buffer to be flushed before it fills up
1616 // (if not, we'd have bad response time)
1618 vector<T_dyninstRPC::trace_batch_buffer_entry> theTraceBatchBuffer (TRACE_BUFFER_SIZE);
1619 unsigned int trace_batch_buffer_next=0;
1621 void flush_trace_batch_buffer(int program) {
1622 // don't need to flush if the batch had no data (this does happen; see
1624 if (trace_batch_buffer_next == 0)
1627 vector<T_dyninstRPC::trace_batch_buffer_entry> copyTraceBatchBuffer(trace_batch_buffer_next);
1628 for (unsigned i=0; i< trace_batch_buffer_next; i++)
1629 copyTraceBatchBuffer[i] = theTraceBatchBuffer[i];
1632 // Now let's do the actual igen call!
1634 tp->batchTraceDataCallbackFunc(program, copyTraceBatchBuffer);
1636 TRACE_BURST_HAS_COMPLETED = false;
1637 trace_batch_buffer_next = 0;
1640 void batchTraceData(int program, int mid, int recordLength,
1643 // Now let's batch this entry.
1644 T_dyninstRPC::trace_batch_buffer_entry &theEntry = theTraceBatchBuffer[trace_batch_buffer_next];
1646 theEntry.length = recordLength;
1647 theEntry.traceRecord = byteArray(recordPtr,recordLength);
1648 trace_batch_buffer_next++;
1650 // We buffer things up and eventually call tp->batchTraceDataCallbackFunc
1652 // Flush the buffer if (1) it is full, or (2) for good response time, after
1654 if (trace_batch_buffer_next >= TRACE_BUFFER_SIZE || TRACE_BURST_HAS_COMPLETED) {
1655 flush_trace_batch_buffer(program);
1660 void metricDefinitionNode::forwardSimpleValue(timeStamp start, timeStamp end,
1661 sampleValue value, unsigned weight,
1665 assert(start + 0.000001 >= (firstRecordTime/MILLION));
1666 assert(end >= (firstRecordTime/MILLION));
1667 assert(end > start);
1669 batchSampleData(id_, start, end, value, weight, internal_met);
1672 void metricDefinitionNode::updateValue(time64 wallTime, int new_cumulative_value) {
1673 // This is an alternative to updateValue(time64, sampleValue); this
1674 // integer-only version should be faster (fewer int-->float conversions)
1675 // and possibly more accurate (since int-->float conversions lose precision)
1677 const timeStamp sampleTime = wallTime / 1000000.0;
1678 // yuck; fp division is expensive!!!
1680 // report only the delta from the last sample
1681 assert(new_cumulative_value >= cumulativeValue_float);
1683 // note: right now, cumulativeValue is a float; we should change it to a union
1684 // of {float, int, long, long long, double}
1685 const float delta_value = new_cumulative_value - cumulativeValue_float;
1686 // note: change delta_value to "int" once we get cumulativeValue_int implemented;
1688 // updating cumulativeValue_float is much easier than the float version of
1690 cumulativeValue_float = new_cumulative_value;
1693 assert(samples.size() == aggregators.size());
1694 for (unsigned lcv=0; lcv < samples.size(); lcv++) {
1695 // call sampleInfo::newValue(). sampleInfo is in the util lib
1696 // (aggregateSample.h/.C)
1697 if (samples[lcv]->firstValueReceived())
1698 samples[lcv]->newValue(sampleTime, delta_value);
1700 assert(new_cumulative_value == delta_value);
1701 // this should be true for the 1st sample
1703 samples[lcv]->firstTimeAndValue(sampleTime, delta_value);
1704 // formerly startTime()
1707 // call sampleInfo::updateAggregateComponent()
1708 aggregators[lcv]->updateAggregateComponent(); // metricDefinitionNode::updateAggregateComponent()
1712 void metricDefinitionNode::updateValue(time64 wallTime,
1715 timeStamp sampleTime = wallTime / 1000000.0;
1716 // note: we can probably do integer division by million quicker
1718 assert(value >= -0.01);
1720 // TODO -- is this ok?
1721 // TODO -- do sampledFuncs work ?
1722 if (style_ == EventCounter) {
1724 // only use delta from last sample.
1725 if (value < cumulativeValue_float) {
1726 if ((value/cumulativeValue_float) < 0.99999) {
1727 assert((value + 0.0001) >= cumulativeValue_float);
1729 // floating point rounding error ignore
1730 cumulativeValue_float = value;
1734 // if (value + 0.0001 < cumulativeValue_float)
1735 // printf ("WARNING: sample went backwards!!!!!\n");
1736 value -= cumulativeValue_float;
1737 cumulativeValue_float += value;
1741 // If style==EventCounter then value is changed. Otherwise, it keeps the
1742 // the current "value" (e.g. SampledFunction case). That's why it is not
1743 // necessary to have an special case for SampledFunction.
1746 assert(samples.size() == aggregators.size());
1747 for (unsigned u = 0; u < samples.size(); u++) {
1748 if (samples[u]->firstValueReceived())
1749 samples[u]->newValue(sampleTime, value);
1751 samples[u]->firstTimeAndValue(sampleTime, value);
1752 //samples[u]->startTime(sampleTime);
1754 aggregators[u]->updateAggregateComponent();
1758 void metricDefinitionNode::updateAggregateComponent()
1760 // currently called (only) by the above routine
1761 sampleInterval ret = aggSample.aggregateValues();
1762 // class aggregateSample is in util lib (aggregateSample.h)
1763 // warning: method aggregateValues() is complex
1766 assert(ret.end > ret.start);
1767 assert(ret.start + 0.000001 >= (firstRecordTime/MILLION));
1768 assert(ret.end >= (firstRecordTime/MILLION));
1769 batchSampleData(id_, ret.start, ret.end, ret.value,
1770 aggSample.numComponents(),false);
1775 // Costs are now reported to paradyn like other metrics (ie. we are not
1776 // calling reportInternalMetrics to deliver cost values, instead we wait
1777 // until we have received a new interval of cost data from each process)
1778 // note: this only works for the CM5 because all cost metrics are sumed
1779 // at the daemons and at paradyn, otherwise the CM5 needs its own version
1780 // of this routine that uses the same aggregate method as the one for paradyn
1782 #ifndef SHM_SAMPLING
1783 void processCost(process *proc, traceHeader *h, costUpdate *s)
1785 // we can probably do integer division by million quicker.
1786 timeStamp newSampleTime = (h->wall / 1000000.0);
1787 timeStamp newProcessTime = (h->process / 1000000.0);
1789 timeStamp lastProcessTime =
1790 totalPredictedCost->getLastSampleProcessTime(proc);
1792 // find the portion of uninstrumented time for this interval
1793 double unInstTime = ((newProcessTime - lastProcessTime)
1794 / (1+currentPredictedCost));
1795 // update predicted cost
1796 // note: currentPredictedCost is the same for all processes
1797 // this should be changed to be computed on a per process basis
1798 sampleValue newPredCost = totalPredictedCost->getCumulativeValue(proc);
1799 newPredCost += (float)(currentPredictedCost*unInstTime);
1800 totalPredictedCost->updateValue(proc,newPredCost,
1801 newSampleTime,newProcessTime);
1802 // update observed cost
1803 observed_cost->updateValue(proc,s->obsCostIdeal,
1804 newSampleTime,newProcessTime);
1806 // update smooth observed cost
1807 smooth_obs_cost->updateSmoothValue(proc,s->obsCostIdeal,
1808 newSampleTime,newProcessTime);
1812 #ifndef SHM_SAMPLING
1813 void processSample(int /* pid */, traceHeader *h, traceSample *s)
1815 // called from processTraceStream (perfStream.C) when a TR_SAMPLE record
1816 // has arrived from the appl.
1818 unsigned mid = s->id.id; // low-level counterId (see primitives.C)
1820 static time64 firstWall = 0;
1822 static bool firstTime = true;
1825 firstWall = h->wall;
1828 metricDefinitionNode *mi; // filled in by find() if found
1829 if (!midToMiMap.find(mid, mi)) { // low-level counterId to metricDefinitionNode
1830 metric_cerr << "TR_SAMPLE id " << s->id.id << " not for valid mi...discarding" << endl;
1834 // metric_cerr << "FROM pid " << pid << " got value " << s->value << " for id " << s->id.id << endl;
1836 // sprintf(errorLine, "sample id %d at time %8.6f = %f\n", s->id.id,
1837 // ((double) *(int*) &h->wall) + (*(((int*) &h->wall)+1))/1000000.0, s->value);
1838 // logLine(errorLine);
1839 mi->updateValue(h->wall, s->value);
1845 * functions to operate on inst request graph.
1848 instReqNode::instReqNode(instPoint *iPoint,
1851 callOrder o, bool iManuallyTrigger) {
1855 instance = NULL; // set when insertInstrumentation() calls addInstFunc()
1856 ast = assignAst(iAst);
1857 manuallyTrigger = iManuallyTrigger;
1861 instReqNode instReqNode::forkProcess(const instReqNode &parentNode,
1862 const dictionary_hash<instInstance*,instInstance*> &map) {
1863 instReqNode ret = instReqNode(parentNode.point, parentNode.ast, parentNode.when,
1865 false // don't manually trigger
1868 if (!map.find(parentNode.instance, ret.instance)) // writes to ret.instance
1874 bool instReqNode::unFork(dictionary_hash<instInstance*,instInstance*> &map) const {
1875 // The fork syscall duplicates all trampolines from the parent into the child. For
1876 // those mi's which we don't want to propagate to the child, this creates a
1877 // problem. We need to remove instrumentation code from the child. This routine
1880 // "this" represents an instReqNode in the PARENT process.
1881 // "map" maps all instInstance*'s of the parent process to instInstance*'s in the
1882 // child process. We modify "map" by setting a value to NULL.
1884 instInstance *parentInstance = getInstance();
1886 instInstance *childInstance;
1887 if (!map.find(parentInstance, childInstance)) // writes to childInstance
1890 vector<unsigned> pointsToCheck; // is it right leaving this empty on a fork()???
1891 deleteInst(childInstance, pointsToCheck);
1893 map[parentInstance] = NULL; // since we've deleted...
1895 return true; // success
1898 bool instReqNode::insertInstrumentation(process *theProc,
1899 returnInstance *&retInstance)
1901 // NEW: We may manually trigger the instrumentation, via a call to postRPCtoDo()
1903 // addInstFunc() is one of the key routines in all paradynd.
1904 // It installs a base tramp at the point (if needed), generates code
1905 // for the tramp, calls inferiorMalloc() in the text heap to get space for it,
1906 // and actually inserts the instrumentation.
1907 instance = addInstFunc(theProc, point, ast, when, order,
1908 false, // false --> don't exclude cost
1911 return (instance != NULL);
1914 void instReqNode::disable(const vector<unsigned> &pointsToCheck)
1916 deleteInst(instance, pointsToCheck);
1920 instReqNode::~instReqNode()
1926 float instReqNode::cost(process *theProc) const
1931 int unitCostInCycles;
1933 unitCostInCycles = ast->cost() + getPointCost(theProc, point) +
1934 getInsnCost(trampPreamble) + getInsnCost(trampTrailer);
1935 // printf("unit cost = %d cycles\n", unitCostInCycles);
1936 unitCost = unitCostInCycles/ cyclesPerSecond;
1937 frequency = getPointFrequency(point);
1938 value = unitCost * frequency;
1942 bool instReqNode::triggerNow(process *theProc, int mid) {
1943 assert(manuallyTrigger);
1945 theProc->postRPCtoDo(ast, false, // don't skip cost
1946 NULL, // no callback fn needed
1949 // the rpc will be launched with a call to launchRPCifAppropriate()
1950 // in the main loop (perfStream.C)
1955 /* ************************************************************************* */
1957 #ifndef SHM_SAMPLING
1958 sampledIntCounterReqNode::sampledIntCounterReqNode(int iValue, int iCounterId,
1959 metricDefinitionNode *iMi,
1960 bool computingCost) :
1962 theSampleId = iCounterId;
1963 initialValue = iValue;
1965 // The following fields are NULL until insertInstrumentation()
1969 if (!computingCost) {
1971 isOk = insertInstrumentation(iMi->proc(), iMi);
1972 assert(isOk && counterPtr!=NULL);
1976 sampledIntCounterReqNode::sampledIntCounterReqNode(const sampledIntCounterReqNode &src,
1978 metricDefinitionNode *,
1980 const dictionary_hash<instInstance*,instInstance*> &map) {
1981 // a dup() routine (call after a fork())
1982 counterPtr = src.counterPtr; // assumes addr spaces have been dup()d.
1984 if (!map.find(src.sampler, this->sampler)) // writes to this->sampler
1987 theSampleId = iCounterId;
1990 temp.id.id = this->theSampleId;
1991 temp.value = initialValue;
1992 writeToInferiorHeap(childProc, temp);
1996 sampledIntCounterReqNode::dup(process *childProc,
1997 metricDefinitionNode *mi,
1999 const dictionary_hash<instInstance*,instInstance*> &map
2001 // duplicate 'this' (allocate w/ new) and return. Call after a fork().
2003 sampledIntCounterReqNode *tmp;
2004 tmp = new sampledIntCounterReqNode(*this, childProc, mi, iCounterId, map);
2010 bool sampledIntCounterReqNode::insertInstrumentation(process *theProc,
2011 metricDefinitionNode *,
2013 // Remember counterPtr and sampler are NULL until this routine
2015 counterPtr = (intCounter*)inferiorMalloc(theProc, sizeof(intCounter), dataHeap);
2016 if (counterPtr == NULL)
2017 return false; // failure!
2019 // initialize the intCounter in the inferior heap
2021 temp.id.id = this->theSampleId;
2022 temp.value = this->initialValue;
2024 writeToInferiorHeap(theProc, temp);
2026 function_base *sampleFunction =
2027 theProc->findOneFunction("DYNINSTsampleValues");
2028 if (!sampleFunction)
2029 sampleFunction = theProc->findOneFunction("_DYNINSTsampleValues");
2030 assert(sampleFunction);
2033 tmp = new AstNode(AstNode::Constant, counterPtr);
2034 ast = new AstNode("DYNINSTreportCounter", tmp);
2037 instPoint *func_entry = (instPoint *)sampleFunction->funcEntry(theProc);
2038 sampler = addInstFunc(theProc, func_entry,
2039 ast, callPreInsn, orderLastAtPoint, false);
2042 return true; // success
2045 void sampledIntCounterReqNode::disable(process *theProc,
2046 const vector<unsigVecType> &pointsToCheck) {
2047 // We used to remove the sample id from midToMiMap here but now the caller is
2048 // responsible for that.
2050 // Remove instrumentation added to DYNINSTsampleValues(), if necessary:
2051 if (sampler != NULL)
2052 ::deleteInst(sampler, getAllTrampsAtPoint(sampler));
2054 // Deallocate space for intCounter in the inferior heap:
2055 assert(counterPtr != NULL);
2056 inferiorFree(theProc, (unsigned)counterPtr, dataHeap, pointsToCheck);
2059 void sampledIntCounterReqNode::writeToInferiorHeap(process *theProc,
2060 const intCounter &dataSrc) const {
2061 // using the contents of "dataSrc", write to the inferior heap at loc
2062 // "counterPtr" via proc->writeDataSpace()
2064 theProc->writeDataSpace(counterPtr, sizeof(intCounter), &dataSrc);
2067 bool sampledIntCounterReqNode::
2068 unFork(dictionary_hash<instInstance*,instInstance*> &map) {
2069 instInstance *parentSamplerInstance = this->sampler;
2071 instInstance *childSamplerInstance;
2072 if (!map.find(parentSamplerInstance, childSamplerInstance))
2075 vector<unsigned> pointsToCheck; // empty on purpose
2076 deleteInst(childSamplerInstance, pointsToCheck);
2078 map[parentSamplerInstance] = NULL;
2085 /* ************************************************************************* */
2089 sampledShmIntCounterReqNode::sampledShmIntCounterReqNode(int iValue,
2091 metricDefinitionNode *iMi,
2095 theSampleId = iCounterId;
2096 initialValue = iValue;
2098 // The following fields are NULL until insertInstrumentation()
2099 allocatedIndex = UINT_MAX;
2100 allocatedLevel = UINT_MAX;
2104 if (!computingCost) {
2106 isOk = insertInstrumentation(iMi->proc(), iMi, doNotSample);
2111 sampledShmIntCounterReqNode::
2112 sampledShmIntCounterReqNode(const sampledShmIntCounterReqNode &src,
2113 process *childProc, metricDefinitionNode *mi,
2114 int iCounterId, const process *parentProc) {
2115 // a dup() routine (call after a fork())
2116 // Assumes that "childProc" has been copied already (e.g., the shm seg was copied).
2118 // Note that the index w/in the inferior heap remains the same, so setting the
2119 // new inferiorCounterPtr isn't too hard. Actually, it's trivial, since other code
2120 // ensures that the new shm segment is placed in exactly the same virtual mem loc
2121 // as the previous one.
2123 // Note that the fastInferiorHeap class's fork ctor will have already copied the
2124 // actual data; we need to fill in new meta-data (new houseKeeping entries).
2126 this->allocatedIndex = src.allocatedIndex;
2127 this->allocatedLevel = src.allocatedLevel;
2129 this->theSampleId = iCounterId; // this is different from the parent's value
2130 this->initialValue = src.initialValue;
2132 superTable &theTable = childProc->getTable();
2134 // since the new shm seg is placed in exactly the same memory location as
2135 // the old one, nothing here should change.
2136 const superTable &theParentTable = parentProc->getTable();
2137 assert(theTable.index2InferiorAddr(0,childProc->threads[0]->get_pd_pos(),allocatedIndex,allocatedLevel)==theParentTable.index2InferiorAddr(0,parentProc->threads[0]->get_pd_pos(),allocatedIndex,allocatedLevel));
2139 for (unsigned i=0; i<childProc->threads.size(); i++) {
2140 // write to the raw item in the inferior heap:
2141 intCounter *localCounterPtr = (intCounter *) theTable.index2LocalAddr(0,childProc->threads[i]->get_pd_pos(),allocatedIndex,allocatedLevel);
2142 const intCounter *localSrcCounterPtr = (const intCounter *) childProc->getParent()->getTable().index2LocalAddr(0,childProc->threads[i]->get_pd_pos(),allocatedIndex,allocatedLevel);
2143 localCounterPtr->value = initialValue;
2144 localCounterPtr->id.id = theSampleId;
2145 localCounterPtr->theSpinner = localSrcCounterPtr->theSpinner;
2146 // in case we're in the middle of an operation
2149 // write HK for this intCounter:
2150 // Note: we don't assert anything about mi->getMId(), because that id has no
2151 // relation to the ids we work with (theSampleId). In fact, we (the sampling code)
2152 // just don't ever care what mi->getMId() is.
2153 assert(theSampleId >= 0);
2154 assert(midToMiMap.defines(theSampleId));
2155 assert(midToMiMap[theSampleId] == mi);
2156 intCounterHK iHKValue(theSampleId, mi);
2157 // the mi differs from the mi of the parent; theSampleId differs too.
2158 theTable.initializeHKAfterForkIntCounter(allocatedIndex, allocatedLevel, iHKValue);
2164 sampledShmIntCounterReqNode::dup(process *childProc,
2165 metricDefinitionNode *mi,
2167 const dictionary_hash<instInstance*,instInstance*> &
2169 // duplicate 'this' (allocate w/ new) and return. Call after a fork().
2171 sampledShmIntCounterReqNode *tmp;
2172 tmp = new sampledShmIntCounterReqNode(*this, childProc, mi, iCounterId, childProc->getParent());
2178 bool sampledShmIntCounterReqNode::insertInstrumentation(process *theProc,
2179 metricDefinitionNode *iMi, bool doNotSample) {
2180 // Remember counterPtr is NULL until this routine gets called.
2181 // WARNING: there will be an assert failure if the applic hasn't yet attached to the
2184 // initialize the intCounter in the inferior heap
2186 iValue.id.id = this->theSampleId;
2187 iValue.value = this->initialValue; // what about initializing 'theSpinner'???
2189 intCounterHK iHKValue(this->theSampleId, iMi);
2191 superTable &theTable = theProc->getTable();
2193 if (!theTable.allocIntCounter(iValue, iHKValue, this->allocatedIndex, this->allocatedLevel, doNotSample))
2194 return false; // failure
2196 return true; // success
2199 void sampledShmIntCounterReqNode::disable(process *theProc,
2200 const vector<unsigVecType> &pointsToCheck) {
2201 // We used to remove the sample id from midToMiMap here but now the caller is
2202 // responsible for that.
2204 superTable &theTable = theProc->getTable();
2206 // Remove from inferior heap; make sure we won't be sampled any more:
2207 vector<unsigned> trampsMaybeUsing;
2208 for (unsigned pointlcv=0; pointlcv < pointsToCheck.size(); pointlcv++)
2209 for (unsigned tramplcv=0; tramplcv < pointsToCheck[pointlcv].size(); tramplcv++)
2210 trampsMaybeUsing += pointsToCheck[pointlcv][tramplcv];
2212 theTable.makePendingFree(0,allocatedIndex,allocatedLevel,trampsMaybeUsing);
2214 #if defined(MT_THREAD)
2215 //NOTE: Not yet implemented for shm sampling! naim 4/23/97
2216 // pdThread *thr = theProc->threads[0];
2217 // thr->CTvector->remove(this->theSampleId, this->position_);
2218 // theProc->updateActiveCT(false,counter);
2224 /* ************************************************************************* */
2226 nonSampledIntCounterReqNode::nonSampledIntCounterReqNode(int iValue,
2228 metricDefinitionNode *iMi,
2229 bool computingCost) :
2231 theSampleId = iCounterId;
2232 initialValue = iValue;
2234 // The following fields are NULL until insertInstrumentation()
2237 if (!computingCost) {
2239 isOk = insertInstrumentation(iMi->proc(), iMi);
2240 assert(isOk && counterPtr!=NULL);
2244 nonSampledIntCounterReqNode::
2245 nonSampledIntCounterReqNode(const nonSampledIntCounterReqNode &src,
2246 process *childProc, metricDefinitionNode *,
2248 // a dup() routine (call after a fork())
2249 counterPtr = src.counterPtr; // assumes addr spaces have been dup()d.
2250 initialValue = src.initialValue;
2251 theSampleId = iCounterId;
2254 temp.id.id = this->theSampleId;
2255 temp.value = this->initialValue;
2256 writeToInferiorHeap(childProc, temp);
2260 nonSampledIntCounterReqNode::dup(process *childProc,
2261 metricDefinitionNode *mi,
2263 const dictionary_hash<instInstance*,instInstance*> &
2265 // duplicate 'this' (allocate w/ new) and return. Call after a fork().
2267 nonSampledIntCounterReqNode *tmp;
2268 tmp = new nonSampledIntCounterReqNode(*this, childProc, mi, iCounterId);
2274 bool nonSampledIntCounterReqNode::insertInstrumentation(process *theProc,
2275 metricDefinitionNode *,
2277 // Remember counterPtr is NULL until this routine gets called.
2278 counterPtr = (intCounter*)inferiorMalloc(theProc, sizeof(intCounter), dataHeap);
2279 if (counterPtr == NULL)
2280 return false; // failure!
2282 // initialize the intCounter in the inferior heap
2284 temp.id.id = this->theSampleId;
2285 temp.value = this->initialValue;
2287 writeToInferiorHeap(theProc, temp);
2289 return true; // success
2292 void nonSampledIntCounterReqNode::disable(process *theProc,
2293 const vector<unsigVecType> &pointsToCheck) {
2294 // We used to remove the sample id from midToMiMap here but now the caller is
2295 // responsible for that.
2297 // Deallocate space for intCounter in the inferior heap:
2298 assert(counterPtr != NULL);
2299 inferiorFree(theProc, (unsigned)counterPtr, dataHeap, pointsToCheck);
2302 void nonSampledIntCounterReqNode::writeToInferiorHeap(process *theProc,
2303 const intCounter &dataSrc) const {
2304 // using the contents of "dataSrc", write to the inferior heap at loc
2305 // "counterPtr" via proc->writeDataSpace()
2307 theProc->writeDataSpace(counterPtr, sizeof(intCounter), &dataSrc);
2310 /* ****************************************************************** */
2312 #ifndef SHM_SAMPLING
2313 sampledTimerReqNode::sampledTimerReqNode(timerType iType, int iCounterId,
2314 metricDefinitionNode *iMi,
2315 bool computingCost) :
2317 theSampleId = iCounterId;
2318 theTimerType = iType;
2320 // The following fields are NULL until insertInstrumentatoin():
2324 if (!computingCost) {
2326 isOk = insertInstrumentation(iMi->proc(), iMi);
2327 assert(isOk && timerPtr!=NULL);
2331 sampledTimerReqNode::sampledTimerReqNode(const sampledTimerReqNode &src,
2333 metricDefinitionNode *,
2335 const dictionary_hash<instInstance*,instInstance*> &map) {
2336 // a dup()-like routine; call after a fork()
2337 timerPtr = src.timerPtr; // assumes addr spaces have been dup()'d
2339 if (!map.find(src.sampler, this->sampler)) // writes to this->sampler
2342 assert(sampler); // makes sense; timers are always sampled, whereas intCounters
2343 // might be just non-sampled predicates.
2345 theSampleId = iCounterId;
2346 theTimerType = src.theTimerType;
2349 P_memset(&temp, '\0', sizeof(tTimer)); /* is this needed? */
2350 temp.id.id = this->theSampleId;
2351 temp.type = this->theTimerType;
2352 temp.normalize = 1000000;
2353 writeToInferiorHeap(childProc, temp);
2355 // WARNING: shouldn't we be resetting the raw value to count=0, start=0,
2356 // total = src.initialValue ??? On the other hand, it's not that
2357 // simple -- if the timer is active in the parent, then it'll be active
2358 // in the child. So how about setting count to src.count, start=now,
2363 sampledTimerReqNode::dup(process *childProc, metricDefinitionNode *mi,
2365 const dictionary_hash<instInstance*,instInstance*> &map
2367 // duplicate 'this' (allocate w/ new) and return. Call after a fork().
2369 sampledTimerReqNode *result = new sampledTimerReqNode(*this, childProc, mi, iCounterId, map);
2372 return NULL; // on failure, return w/o incrementing counterId
2377 bool sampledTimerReqNode::insertInstrumentation(process *theProc,
2378 metricDefinitionNode *,
2380 timerPtr = (tTimer *)inferiorMalloc(theProc, sizeof(tTimer), dataHeap);
2381 if (timerPtr == NULL)
2382 return false; // failure!
2384 // Now let's initialize the newly allocated tTimer in the inferior heap:
2386 P_memset(&temp, '\0', sizeof(tTimer));
2387 temp.id.id = this->theSampleId;
2388 temp.type = this->theTimerType;
2389 temp.normalize = 1000000;
2390 writeToInferiorHeap(theProc, temp);
2392 // Now instrument DYNINSTreportTimer:
2393 function_base *sampleFunction =
2394 theProc->findOneFunction("DYNINSTsampleValues");
2395 if (!sampleFunction)
2396 sampleFunction = theProc->findOneFunction("_DYNINSTsampleValues");
2397 assert(sampleFunction);
2400 tmp = new AstNode(AstNode::Constant, timerPtr);
2401 ast = new AstNode("DYNINSTreportTimer", tmp);
2404 instPoint *func_entry = (instPoint *)sampleFunction->funcEntry(theProc);
2405 sampler = addInstFunc(theProc, func_entry, ast,
2406 callPreInsn, orderLastAtPoint, false);
2409 return true; // successful
2412 void sampledTimerReqNode::disable(process *theProc,
2413 const vector<unsigVecType> &pointsToCheck) {
2414 // We used to remove the sample id from midToMiMap here but now the caller is
2415 // responsible for that.
2417 // Remove instrumentation added to DYNINSTsampleValues(), if necessary:
2418 if (sampler != NULL)
2419 ::deleteInst(sampler, getAllTrampsAtPoint(sampler));
2421 // Deallocate space for tTimer in the inferior heap:
2423 inferiorFree(theProc, (unsigned)timerPtr, dataHeap, pointsToCheck);
2426 void sampledTimerReqNode::writeToInferiorHeap(process *theProc,
2427 const tTimer &dataSrc) const {
2428 // using contents of "dataSrc", a local copy of the data,
2429 // write to inferior heap at loc "timerPtr" via proc->writeDataSpace()
2431 theProc->writeDataSpace(timerPtr, sizeof(tTimer), &dataSrc);
2434 bool sampledTimerReqNode::
2435 unFork(dictionary_hash<instInstance*,instInstance*> &map) {
2436 instInstance *parentSamplerInstance = sampler;
2438 instInstance *childSamplerInstance;
2439 if (!map.find(parentSamplerInstance, childSamplerInstance))
2442 vector<unsigned> pointsToCheck; // empty
2443 deleteInst(childSamplerInstance, pointsToCheck);
2445 map[parentSamplerInstance] = NULL; // since we've deleted...
2452 /* ****************************************************************** */
2455 sampledShmWallTimerReqNode::sampledShmWallTimerReqNode(int iCounterId,
2456 metricDefinitionNode *iMi,
2457 bool computingCost) :
2459 theSampleId = iCounterId;
2461 // The following fields are NULL until insertInstrumentation():
2462 allocatedIndex = UINT_MAX;
2463 allocatedLevel = UINT_MAX;
2467 if (!computingCost) {
2469 isOk = insertInstrumentation(iMi->proc(), iMi);
2474 sampledShmWallTimerReqNode::
2475 sampledShmWallTimerReqNode(const sampledShmWallTimerReqNode &src,
2477 metricDefinitionNode *mi,
2478 int iCounterId, const process *parentProc) {
2479 // a dup()-like routine; call after a fork().
2480 // Assumes that the "childProc" has been duplicated already
2482 // Note that the index w/in the inferior heap remains the same, so setting the new
2483 // inferiorTimerPtr isn't too hard. Actually, it's trivial, since other code
2484 // ensures that the new shm segment is placed in exactly the same virtual mem loc
2485 // as the previous one.
2487 // Note that the fastInferiorHeap class's fork ctor will have already copied the
2488 // actual data; we need to fill in new meta-data (new houseKeeping entries).
2490 allocatedIndex = src.allocatedIndex;
2491 allocatedLevel = src.allocatedLevel;
2493 theSampleId = iCounterId;
2494 assert(theSampleId != src.theSampleId);
2496 superTable &theTable = childProc->getTable();
2498 // since the new shm seg is placed in exactly the same memory location as
2499 // the old one, nothing here should change.
2500 const superTable &theParentTable = parentProc->getTable();
2501 assert(theTable.index2InferiorAddr(1,childProc->threads[0]->get_pd_pos(),allocatedIndex,allocatedLevel)==theParentTable.index2InferiorAddr(1,parentProc->threads[0]->get_pd_pos(),allocatedIndex,allocatedLevel));
2503 // Write new raw value in the inferior heap:
2504 // we set localTimerPtr as follows: protector1 and procetor2 should be copied from
2505 // src. total should be reset to 0. start should be set to now if active else 0.
2506 // counter should be copied from the source.
2507 // NOTE: SINCE WE COPY FROM THE SOURCE, IT'S IMPORTANT THAT ON A FORK, BOTH THE
2508 // PARENT AND CHILD ARE PAUSED UNTIL WE COPY THINGS OVER. THAT THE CHILD IS
2509 // PAUSED IS NOTHING NEW; THAT THE PARENT SHOULD BE PAUSED IS NEW NEWS!
2511 for (unsigned i=0; i<childProc->threads.size(); i++) {
2512 tTimer *localTimerPtr = (tTimer *) theTable.index2LocalAddr(1,childProc->threads[i]->get_pd_pos(),allocatedIndex,allocatedLevel);
2513 const tTimer *srcTimerPtr = (const tTimer *) childProc->getParent()->getTable().index2LocalAddr(1,childProc->threads[i]->get_pd_pos(),allocatedIndex,allocatedLevel);
2515 localTimerPtr->total = 0;
2516 localTimerPtr->counter = srcTimerPtr->counter;
2517 localTimerPtr->id.id = theSampleId;
2518 localTimerPtr->protector1 = srcTimerPtr->protector1;
2519 localTimerPtr->protector2 = srcTimerPtr->protector2;
2521 if (localTimerPtr->counter == 0)
2522 // inactive timer...this is the easy case to copy
2523 localTimerPtr->start = 0; // undefined, really
2525 // active timer...don't copy the start time from the source...make it 'now'
2526 localTimerPtr->start = getCurrWallTime();
2529 // write new HK for this tTimer:
2530 // Note: we don't assert anything about mi->getMId(), because that id has no
2531 // relation to the ids we work with (theSampleId). In fact, we (the sampling code)
2532 // just don't ever care what mi->getMId() is.
2533 assert(theSampleId >= 0);
2534 assert(midToMiMap.defines(theSampleId));
2535 assert(midToMiMap[theSampleId] == mi);
2536 wallTimerHK iHKValue(theSampleId, mi, 0); // is last param right?
2537 // the mi should differ from the mi of the parent; theSampleId differs too.
2538 theTable.initializeHKAfterForkWallTimer(allocatedIndex, allocatedLevel, iHKValue);
2544 sampledShmWallTimerReqNode::dup(process *childProc,
2545 metricDefinitionNode *mi,
2547 const dictionary_hash<instInstance*,instInstance*> &
2549 // duplicate 'this' (allocate w/ new) and return. Call after a fork().
2551 sampledShmWallTimerReqNode *tmp;
2552 tmp = new sampledShmWallTimerReqNode(*this, childProc, mi, iCounterId, childProc->getParent());
2558 bool sampledShmWallTimerReqNode::insertInstrumentation(process *theProc,
2559 metricDefinitionNode *iMi, bool) {
2560 // Remember inferiorTimerPtr is NULL until this routine gets called.
2561 // WARNING: there will be an assert failure if the applic hasn't yet attached to the
2564 // initialize the tTimer in the inferior heap
2566 P_memset(&iValue, '\0', sizeof(tTimer));
2567 iValue.id.id = this->theSampleId;
2569 wallTimerHK iHKValue(this->theSampleId, iMi, 0);
2571 superTable &theTable = theProc->getTable();
2573 if (!theTable.allocWallTimer(iValue, iHKValue, this->allocatedIndex, this->allocatedLevel))
2574 return false; // failure
2579 void sampledShmWallTimerReqNode::disable(process *theProc,
2580 const vector<unsigVecType> &pointsToCheck) {
2581 // We used to remove the sample id from midToMiMap here but now the caller is
2582 // responsible for that.
2584 superTable &theTable = theProc->getTable();
2586 // Remove from inferior heap; make sure we won't be sampled any more:
2587 vector<unsigned> trampsMaybeUsing;
2588 for (unsigned pointlcv=0; pointlcv < pointsToCheck.size(); pointlcv++)
2589 for (unsigned tramplcv=0; tramplcv < pointsToCheck[pointlcv].size(); tramplcv++)
2590 trampsMaybeUsing += pointsToCheck[pointlcv][tramplcv];
2592 theTable.makePendingFree(1,allocatedIndex,allocatedLevel,trampsMaybeUsing);
2594 #if defined(MT_THREAD)
2595 //NOTE: Not yet implemented for shm sampling! naim 4/23/97
2596 // pdThread *thr = theProc->threads[0];
2597 // thr->CTvector->remove(this->theSampleId, this->position_);
2598 // theProc->updateActiveCT(false,wallTimer);
2602 /* ****************************************************************** */
2604 sampledShmProcTimerReqNode::sampledShmProcTimerReqNode(int iCounterId,
2605 metricDefinitionNode *iMi,
2606 bool computingCost) :
2608 theSampleId = iCounterId;
2610 // The following fields are NULL until insertInstrumentatoin():
2611 allocatedIndex = UINT_MAX;
2612 allocatedLevel = UINT_MAX;
2616 if (!computingCost) {
2618 isOk = insertInstrumentation(iMi->proc(), iMi);
2623 sampledShmProcTimerReqNode::
2624 sampledShmProcTimerReqNode(const sampledShmProcTimerReqNode &src,
2626 metricDefinitionNode *mi,
2627 int iCounterId, const process *parentProc) {
2628 // a dup()-like routine; call after a fork()
2629 // Assumes that the "childProc" has been duplicated already
2631 // Note that the index w/in the inferior heap remains the same, so setting the new
2632 // inferiorTimerPtr isn't too hard. Actually, it's trivial, since other code
2633 // ensures that the new shm segment is placed in exactly the same virtual mem loc
2634 // as the previous one.
2636 // Note that the fastInferiorHeap class's fork ctor will have already copied the
2637 // actual data; we need to fill in new meta-data (new houseKeeping entries).
2639 allocatedIndex = src.allocatedIndex;
2640 allocatedLevel = src.allocatedLevel;
2641 theSampleId = iCounterId;
2642 assert(theSampleId != src.theSampleId);
2644 superTable &theTable = childProc->getTable();
2646 // since the new shm seg is placed in exactly the same memory location as
2647 // the old one, nothing here should change.
2648 const superTable &theParentTable = parentProc->getTable();
2649 assert(theTable.index2InferiorAddr(2,childProc->threads[0]->get_pd_pos(),allocatedIndex,allocatedLevel)==theParentTable.index2InferiorAddr(2,parentProc->threads[0]->get_pd_pos(),allocatedIndex,allocatedLevel));
2651 // Write new raw value:
2652 // we set localTimerPtr as follows: protector1 and procetor2 should be copied from
2653 // src. total should be reset to 0. start should be set to now if active else 0.
2654 // counter should be copied from the source.
2655 // NOTE: SINCE WE COPY FROM THE SOURCE, IT'S IMPORTANT THAT ON A FORK, BOTH THE
2656 // PARENT AND CHILD ARE PAUSED UNTIL WE COPY THINGS OVER. THAT THE CHILD IS
2657 // PAUSED IS NOTHING NEW; THAT THE PARENT SHOULD BE PAUSED IS NEW NEWS!
2659 for (unsigned i=0; i<childProc->threads.size(); i++) {
2660 tTimer *localTimerPtr = (tTimer *) theTable.index2LocalAddr(2,childProc->threads[i]->get_pd_pos(),allocatedIndex,allocatedLevel);
2661 const tTimer *srcTimerPtr = (const tTimer *) childProc->getParent()->getTable().index2LocalAddr(2,childProc->threads[i]->get_pd_pos(),allocatedIndex,allocatedLevel);
2663 localTimerPtr->total = 0;
2664 localTimerPtr->counter = srcTimerPtr->counter;
2665 localTimerPtr->id.id = theSampleId;
2666 localTimerPtr->protector1 = srcTimerPtr->protector1;
2667 localTimerPtr->protector2 = srcTimerPtr->protector2;
2669 if (localTimerPtr->counter == 0)
2670 // inactive timer...this is the easy case to copy
2671 localTimerPtr->start = 0; // undefined, really
2673 // active timer...don't copy the start time from the source...make it 'now'
2674 localTimerPtr->start = childProc->getInferiorProcessCPUtime();
2677 // Write new HK for this tTimer:
2678 // Note: we don't assert anything about mi->getMId(), because that id has no
2679 // relation to the ids we work with (theSampleId). In fact, we (the sampling code)
2680 // just don't ever care what mi->getMId() is.
2681 assert(theSampleId >= 0);
2682 assert(midToMiMap.defines(theSampleId));
2683 assert(midToMiMap[theSampleId] == mi);
2684 processTimerHK iHKValue(theSampleId, mi, 0); // is last param right?
2685 // the mi differs from the mi of the parent; theSampleId differs too.
2686 theTable.initializeHKAfterForkProcTimer(allocatedIndex, allocatedLevel, iHKValue);
2692 sampledShmProcTimerReqNode::dup(process *childProc,
2693 metricDefinitionNode *mi,
2695 const dictionary_hash<instInstance*,instInstance*> &
2697 // duplicate 'this' (allocate w/ new) and return. Call after a fork().
2699 sampledShmProcTimerReqNode *tmp;
2700 tmp = new sampledShmProcTimerReqNode(*this, childProc, mi, iCounterId, childProc->getParent());
2706 bool sampledShmProcTimerReqNode::insertInstrumentation(process *theProc,
2707 metricDefinitionNode *iMi, bool) {
2708 // Remember inferiorTimerPtr is NULL until this routine gets called.
2709 // WARNING: there will be an assert failure if the applic hasn't yet attached to the
2712 // initialize the tTimer in the inferior heap
2714 P_memset(&iValue, '\0', sizeof(tTimer));
2715 iValue.id.id = this->theSampleId;
2717 processTimerHK iHKValue(this->theSampleId, iMi, 0);
2719 superTable &theTable = theProc->getTable();
2721 if (!theTable.allocProcTimer(iValue, iHKValue, this->allocatedIndex,this->allocatedLevel))
2722 return false; // failure
2727 void sampledShmProcTimerReqNode::disable(process *theProc,
2728 const vector<unsigVecType> &pointsToCheck) {
2729 // We used to remove the sample id from midToMiMap here but now the caller is
2730 // responsible for that.
2732 superTable &theTable = theProc->getTable();
2734 // Remove from inferior heap; make sure we won't be sampled any more:
2735 vector<unsigned> trampsMaybeUsing;
2736 for (unsigned pointlcv=0; pointlcv < pointsToCheck.size(); pointlcv++)
2737 for (unsigned tramplcv=0; tramplcv < pointsToCheck[pointlcv].size(); tramplcv++)
2738 trampsMaybeUsing += pointsToCheck[pointlcv][tramplcv];
2740 theTable.makePendingFree(2,allocatedIndex,allocatedLevel,trampsMaybeUsing);
2742 #if defined(MT_THREAD)
2743 //NOTE: Not yet implemented for shm sampling! naim 4/23/97
2744 // pdThread *thr = theProc->threads[0];
2745 // thr->CTvector->remove(this->theSampleId, this->position_);
2746 // theProc->updateActiveCT(false,procTimer);
2751 /* **************************** */
2753 void reportInternalMetrics(bool force)
2755 if (isApplicationPaused())
2756 return; // we don't sample when paused (is this right?)
2758 static timeStamp end=0.0;
2760 // see if we have a sample to establish time base.
2761 if (!firstRecordTime) {
2762 //cerr << "reportInternalMetrics: no because firstRecordTime==0" << endl;
2767 end = (timeStamp)firstRecordTime/MILLION;
2769 const timeStamp now = getCurrentTime(false);
2771 // check if it is time for a sample
2772 if (!force && now < end + samplingRate) {
2773 // cerr << "reportInternalMetrics: no because now < end + samplingRate (end=" << end << "; samplingRate=" << samplingRate << "; now=" << now << ")" << endl;
2774 // cerr << "difference is " << (end+samplingRate-now) << endl;
2778 timeStamp start = end;
2781 // TODO -- clean me up, please
2786 for (unsigned u1 = 0; u1 < processVec.size(); u1++) {
2787 if (processVec[u1]->numOfActCounters_is > max1)
2788 max1=processVec[u1]->numOfActCounters_is;
2789 if (processVec[u1]->numOfActProcTimers_is > max2)
2790 max2=processVec[u1]->numOfActProcTimers_is;
2791 if (processVec[u1]->numOfActWallTimers_is > max3)
2792 max3=processVec[u1]->numOfActWallTimers_is;
2794 numOfActCounters_all=max1;
2795 numOfActProcTimers_all=max2;
2796 numOfActWallTimers_all=max3;
2798 unsigned ai_size = internalMetric::allInternalMetrics.size();
2799 for (unsigned u2=0; u2<ai_size; u2++) {
2800 internalMetric *theIMetric = internalMetric::allInternalMetrics[u2];
2801 // Loop thru all enabled instances of this internal metric...
2803 for (unsigned v=0; v < theIMetric->num_enabled_instances(); v++) {
2804 internalMetric::eachInstance &theInstance = theIMetric->getEnabledInstance(v);
2805 // not "const" since bumpCumulativeValueBy() may be called
2807 sampleValue value = (sampleValue) 0;
2808 if (theIMetric->name() == "active_processes") {
2809 //value = (end - start) * activeProcesses;
2810 value = (end - start) * theInstance.getValue();
2811 } else if (theIMetric->name() == "bucket_width") {
2812 //value = (end - start)* theInstance.getValue();
2813 // I would prefer to use (end-start) * theInstance.getValue(); however,
2814 // we've had some problems getting setValue() called in time, thus
2815 // leaving us with getValues() of 0 sometimes. See longer comment in dynrpc.C --ari
2816 extern float currSamplingRate;
2817 value = (end - start) * currSamplingRate;
2818 } else if (theIMetric->name() == "number_of_cpus") {
2819 value = (end - start) * numberOfCPUs;
2820 } else if (theIMetric->name() == "numOfActCounters") {
2821 value = (end - start) * numOfActCounters_all;
2823 } else if (theIMetric->name() == "numOfActProcTimers") {
2824 value = (end - start) * numOfActProcTimers_all;
2826 } else if (theIMetric->name() == "numOfActWallTimers") {
2827 value = (end - start) * numOfActWallTimers_all;
2829 } else if (theIMetric->name() == "infHeapMemAvailable") {
2830 value = (end - start) * inferiorMemAvailable;
2832 } else if (theIMetric->style() == EventCounter) {
2833 value = theInstance.getValue();
2834 // assert((value + 0.0001) >= imp->cumulativeValue);
2835 value -= theInstance.getCumulativeValue();
2836 theInstance.bumpCumulativeValueBy(value);
2837 } else if (theIMetric->style() == SampledFunction) {
2838 value = theInstance.getValue();
2841 theInstance.report(start, end, value);
2842 // calls metricDefinitionNode->forwardSimpleValue()
2847 void disableAllInternalMetrics() {
2848 for (unsigned u=0; u < internalMetric::allInternalMetrics.size(); u++) {
2849 internalMetric *theIMetric = internalMetric::allInternalMetrics[u];
2851 // Now loop thru all the enabled instances of this internal metric...
2852 while (theIMetric->num_enabled_instances() > 0) {
2853 internalMetric::eachInstance &theInstance = theIMetric->getEnabledInstance(0);
2854 tp->endOfDataCollection(theInstance.getMId());
2855 theIMetric->disableInstance(0);
2862 unsigned sampledShmIntCounterReqNode::getInferiorPtr(process *proc) const {
2863 // counterPtr could be NULL if we are building AstNodes just to compute
2864 // the cost - naim 2/18/97
2866 // this routine will dissapear because we can't compute the address
2867 // of the counter/timer without knowing the thread id - naim 3/17/97
2869 if (allocatedIndex == UINT_MAX || allocatedLevel == UINT_MAX) return(0);
2870 assert(proc != NULL);
2871 superTable &theTable = proc->getTable();
2872 // we assume there is only one thread
2873 return((unsigned) theTable.index2InferiorAddr(0,0,allocatedIndex,allocatedLevel));
2876 unsigned sampledShmWallTimerReqNode::getInferiorPtr(process *proc) const {
2877 // counterPtr could be NULL if we are building AstNodes just to compute
2878 // the cost - naim 2/18/97
2880 // this routine will dissapear because we can't compute the address
2881 // of the counter/timer without knowing the thread id - naim 3/17/97
2883 if (allocatedIndex == UINT_MAX || allocatedLevel == UINT_MAX) return(0);
2884 assert(proc != NULL);
2885 superTable &theTable = proc->getTable();
2886 // we assume there is only one thread
2887 return((unsigned) theTable.index2InferiorAddr(1,0,allocatedIndex,allocatedLevel));
2890 unsigned sampledShmProcTimerReqNode::getInferiorPtr(process *proc) const {
2891 // counterPtr could be NULL if we are building AstNodes just to compute
2892 // the cost - naim 2/18/97
2894 // this routine will dissapear because we can't compute the address
2895 // of the counter/timer without knowing the thread id - naim 3/17/97
2897 if (allocatedIndex == UINT_MAX || allocatedLevel == UINT_MAX) return(0);
2898 assert(proc != NULL);
2899 superTable &theTable = proc->getTable();
2900 // we assume there is only one thread
2901 return((unsigned) theTable.index2InferiorAddr(2,0,allocatedIndex,allocatedLevel));