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(0.0), samples(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 string flat_name = metricAndCanonFocus2FlatName(metric_name, canonicalFocus);
299 // first see if it is already defined.
300 dictionary_hash_iter<unsigned, metricDefinitionNode*> mdi(allMIs);
303 * See if we can find the requested metric instance.
304 * Currently this is only used to cache structs built for cost requests
305 * which are then instantiated. This could be used as a general system
306 * to request find sub-metrics that are already.defines and use them to
307 * reduce cost. This would require adding the componenets of an aggregate
308 * into the allMIs list since searching tends to be top down, not bottom
309 * up. This would also require adding a ref count to many of the structures
310 * so they only get deleted when we are really done with them.
315 metricDefinitionNode *mi= NULL;
317 // TODO -- a dictionary search here will be much faster
318 while (mdi.next(key, mi)) {
319 if (mi->getFullName() == flat_name) {
320 metric_cerr << "createMetricInstance: mi with flat_name " << flat_name << " already exists! using it" << endl;
321 return mi; // this metricDefinitionNode has already been defined
325 if (mdl_can_do(metric_name)) {
328 /* select the processes that should be instrumented. We skip process
329 that have exited, and processes that have been created but are not
330 completely initialized yet.
331 If we try to insert instrumentation in a process that is not ready
332 yet, we get a core dump.
333 A process is ready when it is not in neonatal state and the
334 isBootstrappedYet returns true.
336 vector<process*> procs;
338 for (unsigned u = 0; u < processVec.size(); u++) {
339 if (processVec[u]->status()==exited || processVec[u]->status()==neonatal
340 || processVec[u]->isBootstrappedYet())
341 procs += processVec[u];
344 if (procs.size() == 0) {
345 // there are no processes to instrument
346 //printf("createMetricInstance failed, no processes to instrument\n");
351 if (enable) computingCost = false;
352 else computingCost = true;
353 mi = mdl_do(canonicalFocus, metric_name, flat_name, procs, false,
356 metric_cerr << "createMetricInstance failed since mdl_do failed" << endl;
357 metric_cerr << "metric name was " << metric_name << "; focus was ";
358 print_focus(metric_cerr, canonicalFocus);
362 mi=doInternalMetric(canonicalFocus,
363 canonicalFocus, // is this right for component_canon_focus???
364 metric_name,flat_name,enable,matched);
365 // NULL on serious error; -1 if enable was false; -2 if illegal to instr with
366 // given focus [many internal metrics work only for whole program]
368 if (mi == (metricDefinitionNode*)-2) {
369 metric_cerr << "createMetricInstance: internal metric " << metric_name << " isn't defined for focus: ";
370 print_focus(metric_cerr, canonicalFocus);
371 mi = NULL; // straighten up the return value
373 else if (mi == (metricDefinitionNode*)-1) {
374 assert(!enable); // no error msg needed
375 mi = NULL; // straighten up the return value
377 else if (mi == NULL) {
378 // more serious error...do a printout
379 metric_cerr << "createMetricInstance failed since doInternalMetric failed" << endl;
380 metric_cerr << "metric name was " << metric_name << "; focus was ";
381 print_focus(metric_cerr, canonicalFocus);
391 // propagate this metric instance to process p.
392 // p is a process that started after the metric instance was created
393 // note: don't call this routine for a process started via fork or exec, just
394 // for processes started the "normal" way.
395 // "this" is an aggregate mi, not a component one.
397 void metricDefinitionNode::propagateToNewProcess(process *p) {
398 unsigned comp_size = components.size();
401 return; // if there are no components, shouldn't the mi be fried?
403 for (unsigned u = 0; u < comp_size; u++) {
404 if (components[u]->proc() == p) {
405 // The metric is already enabled for this process. This case can
406 // happen when we are starting several processes at the same time.
412 bool internal = false;
414 metricDefinitionNode *mi = NULL;
415 // an aggregate (not component) mi, though we know that it'll contain just
416 // one component. It's that one component that we're really interested in.
417 if (mdl_can_do(met_)) {
418 // Make the unique ID for this metric/focus visible in MDL.
419 string vname = "$globalId";
420 mdl_env::add(vname, false, MDL_T_INT);
421 mdl_env::set(this->getMId(), vname);
423 vector<process *> vp(1,p);
424 mi = mdl_do(focus_, met_, flat_name_, vp, false, false);
426 // internal and cost metrics don't need to be propagated (um, is this correct?)
430 if (mi) { // successfully created new mi
431 assert(mi->components.size() == 1);
432 metricDefinitionNode *theNewComponent = mi->components[0];
434 components += theNewComponent;
435 theNewComponent->aggregators[0] = this;
436 theNewComponent->samples[0] = aggSample.newComponent();
438 theNewComponent->insertInstrumentation();
439 theNewComponent->checkAndInstallInstrumentation();
443 const float cost = mi->cost();
444 if (cost > originalCost_) {
445 currentPredictedCost += cost - originalCost_;
446 originalCost_ = cost;
449 mi->components.resize(0); // protect the new component
454 metricDefinitionNode* metricDefinitionNode::handleExec() {
455 // called by handleExec(), below. See that routine for documentation.
456 // "this" is a component mi.
458 // If this component mi can be (re-)enabled in the new (post-exec) process, then do
459 // so. Else, remove the component mi from aggregators, etc. Returns new component
460 // mi if successful, NULL otherwise.
464 // How can we tell if the mi can be inserted into the "new" (post-exec) process?
465 // A component mi is basically a set of instReqNodes and dataReqNodes. The latter
466 // don't restrict what can be inserted (is this right?); the instReqNodes hold the
467 // key -- we should look at the functions (instPoint's) where code (whose contents
468 // are in AstNode's) would be inserted. Now clearly, the instPoint's must be
469 // checked -- if any one doesn't exist, then the instReqNode and hence the component
470 // mi doesn't belong in the post-exec process. But what about the AstNode's?
471 // Should the code that gets inserted be subject to a similar test? Probably, but
472 // we currently don't do it.
474 // BUT: Even if a process contains a function in both the pre-exec and post-exec
475 // stages, we must assume that the function is IN A DIFFERENT LOCATION IN
476 // THE ADDRESS SPACE. Ick. So the instPoint's can't be trusted and must
477 // be recalculated from scratch. In that regard, this routine is similar to
478 // propagateToNewProcess(), which propagates aggregate mi's to a brand new
479 // process (but which doesn't work for processes started via fork or exec).
480 // The lesson learned is to (ick, ick, ick) call mdl_do() all over again.
481 // This gets really confusing when you consider that a component mi can belong
482 // to several aggregate mi's (e.g. if we represent cpu time for proc 100 then
483 // we can belong to cpu/whole and cpu/proc-100); for which aggregate mi should
484 // we run mdl_do? Any will do, so we can pick arbitrarily (is this right?).
486 // QUESTION: What about internal or cost metrics??? They have aggregate and
487 // component mi's just like normal metrics, right? If that's so, then
488 // they must be propagated too! NOT YET IMPLEMENTED!!!
490 metricDefinitionNode *aggregateMI = this->aggregators[0];
491 metricDefinitionNode *resultCompMI = NULL; // so far...
493 const bool internal = !mdl_can_do(aggregateMI->met_);
495 return NULL; // NOT YET IMPLEMENTED
497 // try to propagate the mi
498 // note: the following code is mostly stolen from propagateToNewProcess(); blame
499 // it for any bugs :)
501 // Make the unique ID for this metric/focus visible in MDL. (?)
502 string vname = "$globalId";
503 mdl_env::add(vname, false, MDL_T_INT);
504 mdl_env::set(aggregateMI->getMId(), vname);
506 vector<process*> vp(1, this->proc());
507 metricDefinitionNode *tempAggMI = mdl_do(aggregateMI->focus_,
509 aggregateMI->flat_name_,
511 true, // fry existing component MI
513 if (tempAggMI == NULL)
514 return NULL; // failure
516 assert(tempAggMI->aggregate_);
518 // okay, it looks like we successfully created a new aggregate mi.
519 // Of course, we're just interested in the (single) component mi contained
520 // within it; it'll replace "this".
522 assert(tempAggMI->components.size() == 1);
523 resultCompMI = tempAggMI->components[0];
525 resultCompMI->aggregators.resize(0);
526 resultCompMI->samples.resize(0);
528 // For each aggregator, go back and find where "this" was a component mi.
529 // When found, replace the ptr to "this" with "theNewComponent".
530 unsigned num_aggregators = aggregators.size();
531 assert(num_aggregators > 0);
532 for (unsigned agglcv=0; agglcv < num_aggregators; agglcv++) {
533 metricDefinitionNode *aggMI = aggregators[agglcv];
536 for (unsigned complcv=0; complcv < aggMI->components.size(); complcv++) {
537 if (aggMI->components[complcv] == this) {
538 aggMI->components[complcv] = resultCompMI;
540 resultCompMI->aggregators += aggMI;
541 resultCompMI->samples += aggMI->aggSample.newComponent();
543 aggMI->aggSample.removeComponent(this->samples[agglcv]);
552 // Now let's actually insert the instrumentation:
554 resultCompMI->insertInstrumentation();
555 resultCompMI->checkAndInstallInstrumentation();
558 // And fry "tempAggMI", but make sure "resultCompMI" isn't fried when we do so
559 tempAggMI->components.resize(0); // protect resultCompMI
560 delete tempAggMI; // good riddance; you were an ugly hack to begin with
565 void metricDefinitionNode::handleExec(process *proc) {
566 // a static member fn.
567 // handling exec is tricky. At the time this routine is called, the "new" process
568 // has been bootstrapped and is ready for stuff to get inserted. No mi's have yet
569 // been propagated, and the data structures (allMIs, allMIComponents, etc.) are still
570 // in their old, pre-exec state, so they show component mi's enabled for this
571 // process, even though they're not (at least not yet). This routines brings things
574 // Algorithm: loop thru all component mi's for this process. If it is possible to
575 // propagate it to the "new" (post-exec) process, then do so. If not, fry the
576 // component mi. An example where a component mi can no longer fit is an mi
577 // specific to, say, function foo(), which (thanks to the exec syscall) no longer
578 // exists in this process. Note that the exec syscall changed the addr space enough
579 // so even if a given routine foo() is present in both the pre-exec and post-exec
580 // process, we must assume that it has MOVED TO A NEW LOCATION, thus making
581 // the component mi's instReqNode's instPoint out-of-date. Ick.
583 vector<metricDefinitionNode*> miComponents = allMIComponents.values();
584 for (unsigned lcv=0; lcv < miComponents.size(); lcv++) {
585 metricDefinitionNode *componentMI = miComponents[lcv];
586 if (componentMI->proc() != proc)
589 forkexec_cerr << "calling handleExec for component "
590 << componentMI->flat_name_ << endl;
592 metricDefinitionNode *replaceWithComponentMI = componentMI->handleExec();
594 if (replaceWithComponentMI == NULL) {
595 forkexec_cerr << "handleExec for component " << componentMI->flat_name_
596 << " failed, so not propagating it" << endl;
597 componentMI->removeThisInstance(); // propagation failed; fry component mi
600 forkexec_cerr << "handleExec for component " << componentMI->flat_name_
601 << " succeeded...it has been propagated" << endl;
602 // new component mi has already been inserted in place of old component mi
603 // in all of its aggregate's component lists. So, not much left to do,
604 // except to update allMIComponents.
606 assert(replaceWithComponentMI->flat_name_ == componentMI->flat_name_);
608 delete componentMI; // old component mi (dtor removes it from allMIComponents)
609 assert(!allMIComponents.defines(replaceWithComponentMI->flat_name_));
610 allMIComponents[replaceWithComponentMI->flat_name_] = replaceWithComponentMI;
615 // called when all components have been removed (because the processes have exited
616 // or exec'd) from "this". "this" is an aggregate mi.
617 void metricDefinitionNode::endOfDataCollection() {
618 assert(components.size() == 0);
620 // flush aggregateSamples
621 sampleInterval ret = aggSample.aggregateValues();
624 assert(ret.end > ret.start);
625 assert(ret.start >= (firstRecordTime/MILLION));
626 assert(ret.end >= (firstRecordTime/MILLION));
627 batchSampleData(id_, ret.start, ret.end, ret.value,
628 aggSample.numComponents(),false);
629 ret = aggSample.aggregateValues();
631 flush_batch_buffer();
632 // trace data streams
633 extern dictionary_hash<unsigned, unsigned> traceOn;
634 for (unsigned w = 0; w<traceOn.keys().size(); w++) {
635 if (traceOn.values()[w]) {
636 extern void batchTraceData(int, int, int, char *);
638 extern bool TRACE_BURST_HAS_COMPLETED;
639 TRACE_BURST_HAS_COMPLETED = true;
640 batchTraceData(0, (k = traceOn.keys()[w]), 0, (char *)NULL);
644 tp->endOfDataCollection(id_);
647 // remove a component from an aggregate.
648 // "this" is an aggregate mi; "comp" is a component mi.
649 void metricDefinitionNode::removeFromAggregate(metricDefinitionNode *comp) {
650 unsigned size = components.size();
651 for (unsigned u = 0; u < size; u++) {
652 if (components[u] == comp) {
653 delete components[u];
654 components[u] = NULL;
655 components[u] = components[size-1];
656 components.resize(size-1);
658 endOfDataCollection();
663 // should always find the right component
667 // remove this component mi from all aggregators it is a component of.
668 // if the aggregate mi no longer has any components then fry the mi aggregate mi.
669 // called by removeFromMetricInstances, below, when a process exits (or exec's)
670 void metricDefinitionNode::removeThisInstance() {
673 // first, remove from allMIComponents (this is new --- is it right?)
674 assert(allMIComponents.defines(flat_name_));
675 allMIComponents.undef(flat_name_);
677 assert(aggregators.size() == samples.size());
678 unsigned aggr_size = aggregators.size();
679 assert(aggr_size > 0);
681 //for (unsigned u = 0; u < aggr_size; u++) {
682 for (unsigned u = 0; u < aggregators.size() && u < samples.size(); u++) {
683 aggregators[u]->aggSample.removeComponent(samples[u]);
684 aggregators[u]->removeFromAggregate(this);
689 // Called when a process exits, to remove the component associated to proc
690 // from all metric instances. (If, after an exec, we never want to carry over
691 // mi's from the pre-exec, then this routine will work there, too. But we try to
692 // carry over mi's whenever appropriate.)
693 // Remove the aggregate metric instances that don't have any components left
694 void removeFromMetricInstances(process *proc) {
695 // Loop through all of the _component_ mi's; for each with component process
696 // of "proc", remove the component mi from its aggregate mi.
697 // Note: imho, there should be a *per-process* vector of mi-components.
699 vector<metricDefinitionNode *> MIs = allMIComponents.values();
700 for (unsigned j = 0; j < MIs.size(); j++) {
701 if (MIs[j]->proc() == proc)
702 MIs[j]->removeThisInstance();
704 costMetric::removeProcessFromAll(proc); // what about internal metrics?
707 /* *************************************************************************** */
709 // obligatory definition of static member vrble:
710 int metricDefinitionNode::counterId=0;
712 dataReqNode *metricDefinitionNode::addSampledIntCounter(int initialValue,
716 dataReqNode *result=NULL;
719 // shared memory sampling of a reported intCounter
720 result = new sampledShmIntCounterReqNode(initialValue,
721 metricDefinitionNode::counterId,
722 this, computingCost, doNotSample);
723 // implicit conversion to base class
725 // non-shared-memory sampling of a reported intCounter
726 result = new sampledIntCounterReqNode(initialValue,
727 metricDefinitionNode::counterId,
728 this, computingCost);
729 // implicit conversion to base class
734 metricDefinitionNode::counterId++;
736 internalMetricCounterId = metricDefinitionNode::counterId;
738 dataRequests += result;
742 dataReqNode *metricDefinitionNode::addUnSampledIntCounter(int initialValue,
743 bool computingCost) {
744 // sampling of a non-reported intCounter (probably just a predicate)
745 // NOTE: In the future, we should probably put un-sampled intcounters
746 // into shared-memory when SHM_SAMPLING is defined. After all, the shared
747 // memory heap is faster.
748 dataReqNode *result = new nonSampledIntCounterReqNode
749 (initialValue, metricDefinitionNode::counterId,
750 this, computingCost);
751 // implicit conversion to base class
754 metricDefinitionNode::counterId++;
756 internalMetricCounterId = metricDefinitionNode::counterId;
758 dataRequests += result;
762 dataReqNode *metricDefinitionNode::addWallTimer(bool computingCost) {
763 dataReqNode *result = NULL;
766 result = new sampledShmWallTimerReqNode(metricDefinitionNode::counterId, this, computingCost);
767 // implicit conversion to base class
769 result = new sampledTimerReqNode(wallTime, metricDefinitionNode::counterId, this, computingCost);
770 // implicit conversion to base class
775 metricDefinitionNode::counterId++;
777 internalMetricCounterId = metricDefinitionNode::counterId;
779 dataRequests += result;
783 dataReqNode *metricDefinitionNode::addProcessTimer(bool computingCost) {
784 dataReqNode *result = NULL;
787 result = new sampledShmProcTimerReqNode(metricDefinitionNode::counterId, this, computingCost);
788 // implicit conversion to base class
790 result = new sampledTimerReqNode(processTime, metricDefinitionNode::counterId, this, computingCost);
791 // implicit conversion to base class
796 metricDefinitionNode::counterId++;
798 internalMetricCounterId = metricDefinitionNode::counterId;
800 dataRequests += result;
804 /* *************************************************************************** */
806 // called when a process forks (by handleFork(), below). "this" is a (component)
807 // mi in the parent process. Duplicate it for the child, with appropriate
808 // changes (i.e. the pid of the component focus name differs), and return the newly
809 // created child mi. "map" maps all instInstance's of the parent to those copied into
812 // Note how beautifully everything falls into place. Consider the case of alarm
813 // sampling with cpu/whole program. Then comes the fork. The parent process has
814 // (0) a tTimer structure allocated in a specific location in the inferior heap,
815 // (1) instrumentation @ main to call startTimer on that ptr, (2) instrumentation in
816 // DYNINSTsampleValues() to call DYNINSTreportTimer on that ptr.
817 // The child process of fork will have ALL of these things in the exact same locations,
818 // which is correct. We want the timer to be located in the same spot; we want
819 // DYNINSTreportTimer to be called on the same pointer; and main() hasn't moved.
821 // So there's not much to do here. We create a new component mi (with same flat name
822 // as in the parent, except for a different pid), and call "forkProcess" for all
823 // dataReqNodes and instReqNodes, none of which have to do anything titanic.
825 metricDefinitionNode *metricDefinitionNode::forkProcess(process *child,
826 const dictionary_hash<instInstance*,instInstance*> &map) const {
827 // The "focus_" member vrble stays the same, because it was always for the
828 // metric as a whole, and not for some component.
830 // But two things must change, because they were component-specific (and the
831 // component has changed processes):
833 // (2) the component focus (not to be confused with plain focus_)
835 // For example, instead of
836 // "/Code/foo.c/myfunc, /Process/100, ...", we should have
837 // "/Code/foo.c/myfunc, /Process/101, ...", because the pid of the child
838 // differs from that of the parent.
840 // The resource structure of a given process is found in the "rid"
841 // field of class process.
842 const resource *parentResource = child->getParent()->rid;
843 const string &parentPartName = parentResource->part_name();
845 const resource *childResource = child->rid;
846 const string &childPartName = childResource->part_name();
848 vector< vector<string> > newComponentFocus = this->component_focus;
849 // we'll change the process, but not the machine name.
850 bool foundProcess = false;
852 for (unsigned hier=0; hier < component_focus.size(); hier++) {
853 if (component_focus[hier][0] == "Process") {
855 assert(component_focus[hier].size() == 2);
856 // since a component focus is by definition specific to some process
858 assert(component_focus[hier][1] == parentPartName);
860 // change the process:
861 newComponentFocus[hier][1] = childPartName;
865 assert(foundProcess);
867 string newComponentFlatName = metricAndCanonFocus2FlatName(met_, newComponentFocus);
869 metricDefinitionNode *mi =
870 new metricDefinitionNode(child,
871 met_, // metric name doesn't change
872 focus_, // focus doesn't change (tho component focus will)
873 newComponentFocus, // this is a change
874 newComponentFlatName, // this is a change
879 metricDefinitionNode::counterId++;
881 forkexec_cerr << "metricDefinitionNode::forkProcess -- component flat name for parent is " << flat_name_ << "; for child is " << mi->flat_name_ << endl;
883 internalMetricCounterId = metricDefinitionNode::counterId;
885 assert(!allMIComponents.defines(newComponentFlatName));
886 allMIComponents[newComponentFlatName] = mi;
888 // Duplicate the dataReqNodes:
889 for (unsigned u1 = 0; u1 < dataRequests.size(); u1++) {
890 // must add to midToMiMap[] before dup() to avoid some assert fails
891 const int newCounterId = metricDefinitionNode::counterId++;
892 // no relation to mi->getMId();
893 forkexec_cerr << "forked dataReqNode going into midToMiMap with id " << newCounterId << endl;
894 assert(!midToMiMap.defines(newCounterId));
895 midToMiMap[newCounterId] = mi;
897 dataReqNode *newNode = dataRequests[u1]->dup(child, mi, newCounterId, map);
898 // remember, dup() is a virtual fn, so the right dup() and hence the
899 // right fork-ctor is called.
902 mi->dataRequests += newNode;
905 // Duplicate the instReqNodes:
906 for (unsigned u2 = 0; u2 < instRequests.size(); u2++) {
907 mi->instRequests += instReqNode::forkProcess(instRequests[u2], map);
910 mi->inserted_ = true;
915 bool metricDefinitionNode::unFork(dictionary_hash<instInstance*, instInstance*> &map,
916 bool unForkInstRequests,
917 bool unForkDataRequests) {
918 // see below handleFork() for explanation of why this routine is needed.
919 // "this" is a component mi for the parent process; we need to remove copied
920 // instrumentation from the _child_ process.
921 // Returns true iff the instrumentation was removed in the child (would be false
922 // if it's not safe to remove the instrumentation in the child because it was
925 // "map" maps instInstances from the parent process to instInstances in the child
928 // We loop thru the instReqNodes of the parent process, unforking each.
929 // In addition, we need to unfork the dataReqNodes, because the alarm-sampled
930 // ones instrument DYNINSTsampleValues.
934 if (unForkInstRequests)
935 for (unsigned lcv=0; lcv < instRequests.size(); lcv++)
936 if (!instRequests[lcv].unFork(map))
937 result = false; // failure
939 if (unForkDataRequests)
940 for (unsigned lcv=0; lcv < dataRequests.size(); lcv++)
941 if (!dataRequests[lcv]->unFork(map))
942 result = false; // failure
948 // called by forkProcess of context.C, just after the fork-constructor was
949 // called for the child process.
950 void metricDefinitionNode::handleFork(const process *parent, process *child,
951 dictionary_hash<instInstance*,instInstance*> &map) {
952 // "map" defines a mapping from all instInstance's of the parent process to
953 // the copied one in the child process. Some of the child process's ones may
954 // get fried by this routine, as it detects that instrumentation has been copied
955 // (by the fork syscall, which we have no control over) which doesn't belong in
956 // the child process and therefore gets deleted manually.
958 // Remember that a given component can be shared by multiple aggregator-mi's,
959 // so be careful about duplicating a component twice. Since we loop through
960 // component mi's instead of aggregate mi's, it's no problem. Note that it's
961 // possible that only a subset of a component-mi's aggregators should get the newly
962 // created child component mi.
964 vector<metricDefinitionNode *> allComponents = allMIComponents.values();
965 for (unsigned complcv=0; complcv < allComponents.size(); complcv++) {
966 metricDefinitionNode *comp = allComponents[complcv];
968 // duplicate the component (create a new one) if it belongs in the
969 // child process. It belongs if any of its aggregate mi's should be
970 // propagated to the child process. An aggregate mi should be propagated
971 // if it wasn't refined to some process.
973 bool shouldBePropagated = false; // so far
974 bool shouldBeUnforkedIfNotPropagated = false; // so far
975 assert(comp->aggregators.size() > 0);
976 for (unsigned agglcv1=0; agglcv1 < comp->aggregators.size(); agglcv1++) {
977 metricDefinitionNode *aggMI = comp->aggregators[agglcv1];
979 if (aggMI->focus_[resource::process].size() == 1) {
980 // wasn't specific to any process
981 shouldBeUnforkedIfNotPropagated = false; // we'll definitely be using it
982 shouldBePropagated = true;
985 else if (comp->proc() == parent)
986 // was specific to parent process, so fork() copied it into the child,
987 // unless it was an internal or cost metric, in which case there was nothing
989 if (!internalMetric::isInternalMetric(aggMI->getMetName()) &&
990 !costMetric::isCostMetric(aggMI->getMetName()))
991 shouldBeUnforkedIfNotPropagated = true;
993 // was specific to other process, so nothing is in the child for it yet
997 if (!shouldBePropagated && shouldBeUnforkedIfNotPropagated) {
998 // this component mi isn't gonna be propagated to the child process, but
999 // the fork syscall left some residue in the child. Delete that residue now.
1000 assert(comp->proc() == parent);
1001 comp->unFork(map, true, true); // also modifies 'map' to remove items
1004 if (!shouldBePropagated)
1007 // Okay, it's time to propagate this component mi to the subset of its aggregate
1008 // mi's which weren't refined to a specific process. If we've gotten to this
1009 // point, then there _is_ at least one such aggregate.
1010 assert(shouldBePropagated);
1011 metricDefinitionNode *newComp = comp->forkProcess(child, map);
1012 // copies instr (well, fork() does this for us), allocs ctr/timer space,
1013 // initializes. Basically, copies dataReqNode's and instReqNode's.
1015 bool foundAgg = false;
1016 for (unsigned agglcv2=0; agglcv2 < comp->aggregators.size(); agglcv2++) {
1017 metricDefinitionNode *aggMI = comp->aggregators[agglcv2];
1018 if (aggMI->focus_[resource::process].size() == 1) {
1019 // this aggregate mi wasn't specific to any process, so it gets the new
1021 aggMI->components += newComp;
1022 newComp->aggregators += aggMI;
1023 newComp->samples += aggMI->aggSample.newComponent();
1031 bool metricDefinitionNode::anythingToManuallyTrigger() const {
1033 for (unsigned i=0; i < components.size(); i++)
1034 if (components[i]->anythingToManuallyTrigger())
1039 for (unsigned i=0; i < instRequests.size(); i++)
1040 if (instRequests[i].anythingToManuallyTrigger())
1048 void metricDefinitionNode::manuallyTrigger() {
1049 assert(anythingToManuallyTrigger());
1052 for (unsigned i=0; i < components.size(); i++)
1053 components[i]->manuallyTrigger();
1056 for (unsigned i=0; i < instRequests.size(); i++)
1057 if (instRequests[i].anythingToManuallyTrigger())
1058 if (!instRequests[i].triggerNow(proc())) {
1059 cerr << "manual trigger failed for an inst request" << endl;
1065 // startCollecting is called by dynRPC::enableDataCollection (or enableDataCollection2)
1067 // startCollecting is a friend of metricDefinitionNode; can it be
1068 // made a member function of metricDefinitionNode instead?
1069 // Especially since it clearly is an integral part of the class;
1070 // in particular, it sets the crucial vrble "id_"
1071 int startCollecting(string& metric_name, vector<u_int>& focus, int id,
1072 vector<process *> &procsToCont)
1074 bool internal = false;
1076 // Make the unique ID for this metric/focus visible in MDL.
1077 string vname = "$globalId";
1078 mdl_env::add(vname, false, MDL_T_INT);
1079 mdl_env::set(id, vname);
1081 metricDefinitionNode *mi = createMetricInstance(metric_name, focus,
1085 //cerr << "startCollecting for " << metric_name << " failed because createMetricInstance failed" << endl;
1091 assert(!allMIs.defines(mi->id_));
1092 allMIs[mi->id_] = mi;
1094 const float cost = mi->cost();
1095 mi->originalCost_ = cost;
1097 currentPredictedCost += cost;
1100 // enable timing stuff: also code in insertInstrumentation()
1101 u_int start_size = test_heapsize;
1102 printf("ENABLE: %d %s %s\n",start_size,
1103 (mi->getMetName()).string_of(),
1104 (mi->getFullName()).string_of());
1105 static timer inTimer;
1112 // pause processes that are running and add them to procsToCont.
1113 // We don't rerun the processes after we insert instrumentation,
1114 // this will be done by our caller, after all instrumentation
1115 // has been inserted.
1116 for (unsigned u = 0; u < mi->components.size(); u++) {
1117 process *p = mi->components[u]->proc();
1118 if (p->status() == running && p->pause()) {
1124 mi->insertInstrumentation(); // calls pause and unpause (this could be a bug, since the next line should be allowed to execute before the unpause!!!)
1125 mi->checkAndInstallInstrumentation();
1127 // Now that the timers and counters have been allocated on the heap, and
1128 // the instrumentation added, we can manually execute instrumentation
1129 // we may have missed at $start.entry. But has the process been paused
1130 // all this time? Hopefully so; otherwise things can get screwy.
1132 if (mi->anythingToManuallyTrigger()) {
1133 process *theProc = mi->components[0]->proc();
1136 bool alreadyRunning = (theProc->status_ == running);
1141 mi->manuallyTrigger();
1144 theProc->continueProc(); // the continue will trigger our code
1146 ; // the next time the process continues, we'll trigger our code
1152 if(!start_size) start_size = test_heapsize;
1153 printf("It took %f:user %f:system %f:wall seconds heap_left: %d used %d\n"
1154 , inTimer.usecs(), inTimer.ssecs(), inTimer.wsecs(),
1155 test_heapsize,start_size-test_heapsize);
1158 metResPairsEnabled++;
1162 float guessCost(string& metric_name, vector<u_int>& focus) {
1163 // called by dynrpc.C (getPredictedDataCost())
1165 metricDefinitionNode *mi = createMetricInstance(metric_name, focus, false, internal);
1167 //metric_cerr << "guessCost returning 0.0 since createMetricInstance failed" << endl;
1171 float cost = mi->cost();
1172 // delete the metric instance, if it is not being used
1173 if (!allMIs.defines(mi->getMId()))
1179 bool metricDefinitionNode::insertInstrumentation()
1181 // returns true iff successful
1188 unsigned c_size = components.size();
1189 for (unsigned u=0; u<c_size; u++)
1190 if (!components[u]->insertInstrumentation())
1191 return false; // shouldn't we try to undo what's already put in?
1193 bool needToCont = proc_->status() == running;
1194 bool res = proc_->pause();
1198 // Loop thru "dataRequests", an array of (ptrs to) dataReqNode:
1199 // Here we allocate ctrs/timers in the inferior heap but don't
1200 // stick in any code, except (if appropriate) that we'll instrument the
1201 // application's alarm-handler when not shm sampling.
1202 unsigned size = dataRequests.size();
1203 for (unsigned u=0; u<size; u++) {
1204 // the following allocs an object in inferior heap and arranges for
1205 // it to be alarm sampled, if appropriate.
1206 // Note: this is not necessary anymore because we are allocating the
1207 // space when the constructor for dataReqNode is called. This was
1208 // done for the dyninstAPI - naim 2/18/97
1209 //if (!dataRequests[u]->insertInstrumentation(proc_, this))
1210 // return false; // shouldn't we try to undo what's already put in?
1212 unsigned mid = dataRequests[u]->getSampleId();
1213 assert(!midToMiMap.defines(mid));
1214 midToMiMap[mid] = this;
1217 // Loop thru "instRequests", an array of instReqNode:
1218 // (Here we insert code instrumentation, tramps, etc. via addInstFunc())
1219 for (unsigned u1=0; u1<instRequests.size(); u1++) {
1220 // NEW: the following may also manually trigger the instrumentation
1222 returnInstance *retInst=NULL;
1223 if (!instRequests[u1].insertInstrumentation(proc_, retInst))
1224 return false; // shouldn't we try to undo what's already put in?
1227 returnInsts += retInst;
1231 proc_->continueProc();
1237 bool metricDefinitionNode::checkAndInstallInstrumentation() {
1238 bool needToCont = false;
1240 if (installed_) return(true);
1245 unsigned c_size = components.size();
1246 for (unsigned u=0; u<c_size; u++)
1247 components[u]->checkAndInstallInstrumentation();
1249 needToCont = proc_->status() == running;
1250 if (!proc_->pause()) {
1251 cerr << "checkAnd... pause failed" << endl; cerr.flush();
1255 vector<Address> pc = proc_->walkStack();
1257 // for(u_int i=0; i < pc.size(); i++){
1258 // printf("frame %d: pc = 0x%x\n",i,pc[i]);
1261 unsigned rsize = returnInsts.size();
1262 u_int max_index = 0; // first frame where it is safe to install instr
1263 bool delay_install = false; // true if some instr. needs to be delayed
1264 vector<bool> delay_elm(rsize); // wch instr. to delay
1265 // for each inst point walk the stack to determine if it can be
1266 // inserted now (it can if it is not currently on the stack)
1267 // If some can not be inserted, then find the first safe point on
1268 // the stack where all can be inserted, and set a break point
1269 for (unsigned u=0; u<rsize; u++) {
1271 bool installSafe = returnInsts[u] -> checkReturnInstance(pc,index);
1272 if ((!installSafe) && (index > max_index)) max_index = index;
1275 //cerr << "installSafe!" << endl;
1276 returnInsts[u] -> installReturnInstance(proc_);
1277 delay_elm[u] = false;
1279 delay_install = true;
1280 delay_elm[u] = true;
1284 // get rid of pathological cases...caused by threaded applications
1285 // TODO: this should be fixed to do something smarter
1286 if((max_index > 0) && ((max_index+1) >= pc.size())){
1288 //printf("max_index changed: %d\n",max_index);
1290 if((max_index > 0) && (pc[max_index+1] == 0)){
1292 //printf("max_index changed: %d\n",max_index);
1294 Address pc2 = pc[max_index+1];
1295 for(u_int i=0; i < rsize; i++){
1297 returnInsts[i]->addToReturnWaitingList(pc2, proc_);
1302 if (needToCont) proc_->continueProc();
1307 float metricDefinitionNode::cost() const
1311 unsigned c_size = components.size();
1312 for (unsigned u=0; u<c_size; u++) {
1313 float nc = components[u]->cost();
1314 if (nc > ret) ret = nc;
1317 for (unsigned u=0; u<instRequests.size(); u++)
1318 ret += instRequests[u].cost(proc_);
1323 void metricDefinitionNode::disable()
1325 // check for internal metrics
1327 unsigned ai_size = internalMetric::allInternalMetrics.size();
1328 for (unsigned u=0; u<ai_size; u++) {
1329 internalMetric *theIMetric = internalMetric::allInternalMetrics[u];
1330 if (theIMetric->disableByMetricDefinitionNode(this)) {
1331 //logLine("disabled internal metric\n");
1336 // check for cost metrics
1337 for (unsigned i=0; i<costMetric::allCostMetrics.size(); i++){
1338 if (costMetric::allCostMetrics[i]->node == this) {
1339 costMetric::allCostMetrics[i]->disable();
1340 //logLine("disabled cost metric\n");
1344 if (!inserted_) return;
1348 /* disable components of aggregate metrics */
1349 // unsigned c_size = components.size();
1350 //for (unsigned u=0; u<c_size; u++) {
1351 for (unsigned u=0; u<components.size(); u++) {
1352 //components[u]->disable();
1353 metricDefinitionNode *m = components[u];
1354 unsigned aggr_size = m->aggregators.size();
1355 assert(aggr_size == m->samples.size());
1356 for (unsigned u1=0; u1 < aggr_size; u1++) {
1357 if (m->aggregators[u1] == this) {
1358 m->aggregators[u1] = m->aggregators[aggr_size-1];
1359 m->aggregators.resize(aggr_size-1);
1360 m->samples[u1] = m->samples[aggr_size-1];
1361 m->samples.resize(aggr_size-1);
1365 assert(m->aggregators.size() == aggr_size-1);
1366 // disable component only if it is not being shared
1367 if (aggr_size == 1) {
1373 vector<unsigVecType> pointsToCheck;
1374 for (unsigned u1=0; u1<instRequests.size(); u1++) {
1375 unsigVecType pointsForThisRequest =
1376 getAllTrampsAtPoint(instRequests[u1].getInstance());
1377 pointsToCheck += pointsForThisRequest;
1379 instRequests[u1].disable(pointsForThisRequest); // calls deleteInst()
1382 for (unsigned u=0; u<dataRequests.size(); u++) {
1383 unsigned mid = dataRequests[u]->getSampleId();
1384 dataRequests[u]->disable(proc_, pointsToCheck); // deinstrument
1385 assert(midToMiMap.defines(mid));
1386 midToMiMap.undef(mid);
1391 void metricDefinitionNode::removeComponent(metricDefinitionNode *comp) {
1392 assert(!comp->aggregate_);
1393 unsigned aggr_size = comp->aggregators.size();
1394 unsigned found = aggr_size;
1396 if (aggr_size == 0) {
1401 // component has more than one aggregator. Remove this from list of aggregators
1402 for (unsigned u = 0; u < aggr_size; u++) {
1403 if (comp->aggregators[u] == this) {
1408 if (found == aggr_size)
1410 assert(found < aggr_size);
1411 assert(aggr_size == comp->samples.size());
1412 comp->aggregators[found] = comp->aggregators[aggr_size-1];
1413 comp->aggregators.resize(aggr_size-1);
1414 comp->samples[found] = comp->samples[aggr_size-1];
1415 comp->samples.resize(aggr_size-1);
1417 if (aggr_size == 1) {
1424 metricDefinitionNode::~metricDefinitionNode()
1427 /* delete components of aggregate metrics */
1428 unsigned c_size = components.size();
1429 for (unsigned u=0; u<c_size; u++)
1430 removeComponent(components[u]);
1431 //delete components[u];
1432 components.resize(0);
1434 allMIComponents.undef(flat_name_);
1435 for (unsigned u=0; u<dataRequests.size(); u++) {
1436 delete dataRequests[u];
1438 dataRequests.resize(0);
1442 void metricDefinitionNode::cleanup_drn()
1444 // we assume that it is safe to delete a dataReqNode at this point,
1445 // otherwise, we would need to do something similar as in the disable
1446 // method for metricDefinitionNode - naim
1447 vector<unsigVecType> pointsToCheck;
1448 for (unsigned u=0; u<dataRequests.size(); u++) {
1449 dataRequests[u]->disable(proc_, pointsToCheck); // deinstrument
1453 // NOTE: This stuff (flush_batch_buffer() and batchSampleData()) belongs
1454 // in perfStream.C; this is an inappropriate file.
1456 //////////////////////////////////////////////////////////////////////////////
1457 // Buffer the samples before we actually send it //
1458 // Send it when the buffers are full //
1459 // or, send it when the last sample in the interval has arrived. //
1460 //////////////////////////////////////////////////////////////////////////////
1462 const unsigned SAMPLE_BUFFER_SIZE = (1*1024)/sizeof(T_dyninstRPC::batch_buffer_entry);
1463 bool BURST_HAS_COMPLETED = false;
1464 // set to true after a burst (after a processTraceStream(), or sampleNodes for
1465 // the CM5), which will force the buffer to be flushed before it fills up
1466 // (if not, we'd have bad response time)
1468 vector<T_dyninstRPC::batch_buffer_entry> theBatchBuffer (SAMPLE_BUFFER_SIZE);
1469 unsigned int batch_buffer_next=0;
1471 // The following routines (flush_batch_buffer() and batchSampleData() are
1472 // in an inappropriate src file...move somewhere more appropriate)
1473 void flush_batch_buffer() {
1474 // don't need to flush if the batch had no data (this does happen; see
1476 if (batch_buffer_next == 0)
1479 // alloc buffer of the exact size to make communication
1480 // more efficient. Why don't we send theBatchBuffer with a count?
1481 // This would work but would always (in the igen call) copy the entire
1482 // vector. This solution has the downside of calling new but is not too bad
1484 vector<T_dyninstRPC::batch_buffer_entry> copyBatchBuffer(batch_buffer_next);
1485 assert(copyBatchBuffer.size() <= theBatchBuffer.size());
1486 for (unsigned i=0; i< batch_buffer_next; i++) {
1487 copyBatchBuffer[i] = theBatchBuffer[i];
1492 t1=getCurrentTime(false);
1495 // Now let's do the actual igen call!
1496 tp->batchSampleDataCallbackFunc(0, copyBatchBuffer);
1499 t2=getCurrentTime(false);
1500 if ((float)(t2-t1) > 15.0) {
1501 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));
1506 BURST_HAS_COMPLETED = false;
1507 batch_buffer_next = 0;
1510 void batchSampleData(int mid, double startTimeStamp,
1511 double endTimeStamp, double value, unsigned val_weight,
1512 bool internal_metric)
1514 // This routine is called where we used to call tp->sampleDataCallbackFunc.
1515 // We buffer things up and eventually call tp->batchSampleDataCallbackFunc
1518 char myLogBuffer[120] ;
1519 sprintf(myLogBuffer, "mid %d, value %g\n", mid, value) ;
1520 logLine(myLogBuffer) ;
1523 // Flush the buffer if (1) it is full, or (2) for good response time, after
1525 if (batch_buffer_next >= SAMPLE_BUFFER_SIZE || BURST_HAS_COMPLETED)
1526 flush_batch_buffer();
1528 // Now let's batch this entry.
1529 T_dyninstRPC::batch_buffer_entry &theEntry = theBatchBuffer[batch_buffer_next];
1531 theEntry.startTimeStamp = startTimeStamp;
1532 theEntry.endTimeStamp = endTimeStamp;
1533 theEntry.value = value;
1534 theEntry.weight = val_weight;
1535 theEntry.internal_met = internal_metric;
1536 batch_buffer_next++;
1539 //////////////////////////////////////////////////////////////////////////////
1540 // Buffer the traces before we actually send it //
1541 // Send it when the buffers are full //
1542 // or, send it when the last sample in the interval has arrived. //
1543 //////////////////////////////////////////////////////////////////////////////
1545 const unsigned TRACE_BUFFER_SIZE = 10;
1546 bool TRACE_BURST_HAS_COMPLETED = false;
1547 // set to true after a burst (after a processTraceStream(), or sampleNodes for
1548 // the CM5), which will force the buffer to be flushed before it fills up
1549 // (if not, we'd have bad response time)
1551 vector<T_dyninstRPC::trace_batch_buffer_entry> theTraceBatchBuffer (TRACE_BUFFER_SIZE);
1552 unsigned int trace_batch_buffer_next=0;
1554 void flush_trace_batch_buffer(int program) {
1555 // don't need to flush if the batch had no data (this does happen; see
1557 if (trace_batch_buffer_next == 0)
1560 vector<T_dyninstRPC::trace_batch_buffer_entry> copyTraceBatchBuffer(trace_batch_buffer_next);
1561 for (unsigned i=0; i< trace_batch_buffer_next; i++)
1562 copyTraceBatchBuffer[i] = theTraceBatchBuffer[i];
1565 // Now let's do the actual igen call!
1567 tp->batchTraceDataCallbackFunc(program, copyTraceBatchBuffer);
1569 TRACE_BURST_HAS_COMPLETED = false;
1570 trace_batch_buffer_next = 0;
1573 void batchTraceData(int program, int mid, int recordLength,
1576 // Now let's batch this entry.
1577 T_dyninstRPC::trace_batch_buffer_entry &theEntry = theTraceBatchBuffer[trace_batch_buffer_next];
1579 theEntry.length = recordLength;
1580 theEntry.traceRecord = byteArray(recordPtr,recordLength);
1581 trace_batch_buffer_next++;
1583 // We buffer things up and eventually call tp->batchTraceDataCallbackFunc
1585 // Flush the buffer if (1) it is full, or (2) for good response time, after
1587 if (trace_batch_buffer_next >= TRACE_BUFFER_SIZE || TRACE_BURST_HAS_COMPLETED) {
1588 flush_trace_batch_buffer(program);
1593 void metricDefinitionNode::forwardSimpleValue(timeStamp start, timeStamp end,
1594 sampleValue value, unsigned weight,
1598 assert(start + 0.000001 >= (firstRecordTime/MILLION));
1599 assert(end >= (firstRecordTime/MILLION));
1600 assert(end > start);
1602 batchSampleData(id_, start, end, value, weight, internal_met);
1605 void metricDefinitionNode::updateValue(time64 wallTime,
1608 timeStamp sampleTime = wallTime / 1000000.0;
1609 // note: we can probably do integer division by million quicker
1611 assert(value >= -0.01);
1613 // TODO -- is this ok?
1614 // TODO -- do sampledFuncs work ?
1615 if (style_ == EventCounter) {
1617 // only use delta from last sample.
1618 if (value < cumulativeValue) {
1619 if ((value/cumulativeValue) < 0.99999) {
1620 assert((value + 0.0001) >= cumulativeValue);
1622 // floating point rounding error ignore
1623 cumulativeValue = value;
1627 // if (value + 0.0001 < cumulativeValue)
1628 // printf ("WARNING: sample went backwards!!!!!\n");
1629 value -= cumulativeValue;
1630 cumulativeValue += value;
1634 // If style==EventCounter then value is changed. Otherwise, it keeps the
1635 // the current "value" (e.g. SampledFunction case). That's why it is not
1636 // necessary to have an special case for SampledFunction.
1639 assert(samples.size() == aggregators.size());
1640 for (unsigned u = 0; u < samples.size(); u++) {
1641 if (samples[u]->firstValueReceived())
1642 samples[u]->newValue(sampleTime, value);
1644 samples[u]->startTime(sampleTime);
1646 aggregators[u]->updateAggregateComponent();
1650 void metricDefinitionNode::updateAggregateComponent()
1652 // currently called (only) by the above routine
1653 sampleInterval ret = aggSample.aggregateValues();
1655 assert(ret.end > ret.start);
1656 assert(ret.start + 0.000001 >= (firstRecordTime/MILLION));
1657 assert(ret.end >= (firstRecordTime/MILLION));
1658 batchSampleData(id_, ret.start, ret.end, ret.value,
1659 aggSample.numComponents(),false);
1662 // metric_cerr << "sorry, ret.valid false so not batching sample data" << endl;
1667 // Costs are now reported to paradyn like other metrics (ie. we are not
1668 // calling reportInternalMetrics to deliver cost values, instead we wait
1669 // until we have received a new interval of cost data from each process)
1670 // note: this only works for the CM5 because all cost metrics are sumed
1671 // at the daemons and at paradyn, otherwise the CM5 needs its own version
1672 // of this routine that uses the same aggregate method as the one for paradyn
1674 #ifndef SHM_SAMPLING
1675 void processCost(process *proc, traceHeader *h, costUpdate *s)
1677 // we can probably do integer division by million quicker.
1678 timeStamp newSampleTime = (h->wall / 1000000.0);
1679 timeStamp newProcessTime = (h->process / 1000000.0);
1681 timeStamp lastProcessTime =
1682 totalPredictedCost->getLastSampleProcessTime(proc);
1684 // find the portion of uninstrumented time for this interval
1685 double unInstTime = ((newProcessTime - lastProcessTime)
1686 / (1+currentPredictedCost));
1687 // update predicted cost
1688 // note: currentPredictedCost is the same for all processes
1689 // this should be changed to be computed on a per process basis
1690 sampleValue newPredCost = totalPredictedCost->getCumulativeValue(proc);
1691 newPredCost += (float)(currentPredictedCost*unInstTime);
1692 totalPredictedCost->updateValue(proc,newPredCost,
1693 newSampleTime,newProcessTime);
1694 // update observed cost
1695 observed_cost->updateValue(proc,s->obsCostIdeal,
1696 newSampleTime,newProcessTime);
1698 // update smooth observed cost
1699 smooth_obs_cost->updateSmoothValue(proc,s->obsCostIdeal,
1700 newSampleTime,newProcessTime);
1704 #ifndef SHM_SAMPLING
1705 void processSample(int /* pid */, traceHeader *h, traceSample *s)
1707 // called from processTraceStream (perfStream.C) when a TR_SAMPLE record
1708 // has arrived from the appl.
1710 unsigned mid = s->id.id; // low-level counterId (see primitives.C)
1712 static time64 firstWall = 0;
1714 static bool firstTime = true;
1717 firstWall = h->wall;
1720 metricDefinitionNode *mi; // filled in by find() if found
1721 if (!midToMiMap.find(mid, mi)) { // low-level counterId to metricDefinitionNode
1722 metric_cerr << "TR_SAMPLE id " << s->id.id << " not for valid mi...discarding" << endl;
1726 // metric_cerr << "FROM pid " << pid << " got value " << s->value << " for id " << s->id.id << endl;
1728 // sprintf(errorLine, "sample id %d at time %8.6f = %f\n", s->id.id,
1729 // ((double) *(int*) &h->wall) + (*(((int*) &h->wall)+1))/1000000.0, s->value);
1730 // logLine(errorLine);
1731 mi->updateValue(h->wall, s->value);
1737 * functions to operate on inst request graph.
1740 instReqNode::instReqNode(instPoint *iPoint,
1743 callOrder o, bool iManuallyTrigger) {
1747 instance = NULL; // set when insertInstrumentation() calls addInstFunc()
1748 ast = assignAst(iAst);
1749 manuallyTrigger = iManuallyTrigger;
1753 instReqNode instReqNode::forkProcess(const instReqNode &parentNode,
1754 const dictionary_hash<instInstance*,instInstance*> &map) {
1755 instReqNode ret = instReqNode(parentNode.point, parentNode.ast, parentNode.when,
1757 false // don't manually trigger
1760 if (!map.find(parentNode.instance, ret.instance)) // writes to ret.instance
1766 bool instReqNode::unFork(dictionary_hash<instInstance*,instInstance*> &map) const {
1767 // The fork syscall duplicates all trampolines from the parent into the child. For
1768 // those mi's which we don't want to propagate to the child, this creates a
1769 // problem. We need to remove instrumentation code from the child. This routine
1772 // "this" represents an instReqNode in the PARENT process.
1773 // "map" maps all instInstance*'s of the parent process to instInstance*'s in the
1774 // child process. We modify "map" by setting a value to NULL.
1776 instInstance *parentInstance = getInstance();
1778 instInstance *childInstance;
1779 if (!map.find(parentInstance, childInstance)) // writes to childInstance
1782 vector<unsigned> pointsToCheck; // is it right leaving this empty on a fork()???
1783 deleteInst(childInstance, pointsToCheck);
1785 map[parentInstance] = NULL; // since we've deleted...
1787 return true; // success
1790 bool instReqNode::insertInstrumentation(process *theProc,
1791 returnInstance *&retInstance)
1793 // NEW: We may manually trigger the instrumentation, via a call to postRPCtoDo()
1795 // addInstFunc() is one of the key routines in all paradynd.
1796 // It installs a base tramp at the point (if needed), generates code
1797 // for the tramp, calls inferiorMalloc() in the text heap to get space for it,
1798 // and actually inserts the instrumentation.
1799 instance = addInstFunc(theProc, point, ast, when, order,
1800 false, // false --> don't exclude cost
1803 return (instance != NULL);
1806 void instReqNode::disable(const vector<unsigned> &pointsToCheck)
1808 deleteInst(instance, pointsToCheck);
1812 instReqNode::~instReqNode()
1818 float instReqNode::cost(process *theProc) const
1823 int unitCostInCycles;
1825 unitCostInCycles = ast->cost() + getPointCost(theProc, point) +
1826 getInsnCost(trampPreamble) + getInsnCost(trampTrailer);
1827 // printf("unit cost = %d cycles\n", unitCostInCycles);
1828 unitCost = unitCostInCycles/ cyclesPerSecond;
1829 frequency = getPointFrequency(point);
1830 value = unitCost * frequency;
1834 bool instReqNode::triggerNow(process *theProc) {
1835 assert(manuallyTrigger);
1837 theProc->postRPCtoDo(ast, false, // don't skip cost
1838 NULL, // no callback fn needed
1841 // the rpc will be launched with a call to launchRPCifAppropriate()
1842 // in the main loop (perfStream.C)
1847 /* ************************************************************************* */
1849 #ifndef SHM_SAMPLING
1850 sampledIntCounterReqNode::sampledIntCounterReqNode(int iValue, int iCounterId,
1851 metricDefinitionNode *iMi,
1852 bool computingCost) :
1854 theSampleId = iCounterId;
1855 initialValue = iValue;
1857 // The following fields are NULL until insertInstrumentation()
1861 if (!computingCost) {
1863 isOk = insertInstrumentation(iMi->proc(), iMi);
1864 assert(isOk && counterPtr!=NULL);
1868 sampledIntCounterReqNode::sampledIntCounterReqNode(const sampledIntCounterReqNode &src,
1870 metricDefinitionNode *,
1872 const dictionary_hash<instInstance*,instInstance*> &map) {
1873 // a dup() routine (call after a fork())
1874 counterPtr = src.counterPtr; // assumes addr spaces have been dup()d.
1876 if (!map.find(src.sampler, this->sampler)) // writes to this->sampler
1879 theSampleId = iCounterId;
1882 temp.id.id = this->theSampleId;
1883 temp.value = initialValue;
1884 writeToInferiorHeap(childProc, temp);
1888 sampledIntCounterReqNode::dup(process *childProc,
1889 metricDefinitionNode *mi,
1891 const dictionary_hash<instInstance*,instInstance*> &map
1893 // duplicate 'this' (allocate w/ new) and return. Call after a fork().
1895 sampledIntCounterReqNode *tmp;
1896 tmp = new sampledIntCounterReqNode(*this, childProc, mi, iCounterId, map);
1902 bool sampledIntCounterReqNode::insertInstrumentation(process *theProc,
1903 metricDefinitionNode *,
1905 // Remember counterPtr and sampler are NULL until this routine
1907 counterPtr = (intCounter*)inferiorMalloc(theProc, sizeof(intCounter), dataHeap);
1908 if (counterPtr == NULL)
1909 return false; // failure!
1911 // initialize the intCounter in the inferior heap
1913 temp.id.id = this->theSampleId;
1914 temp.value = this->initialValue;
1916 writeToInferiorHeap(theProc, temp);
1918 function_base *sampleFunction =
1919 theProc->findOneFunction("DYNINSTsampleValues");
1920 assert(sampleFunction);
1923 tmp = new AstNode(AstNode::Constant, counterPtr);
1924 ast = new AstNode("DYNINSTreportCounter", tmp);
1927 instPoint *func_entry = (instPoint *)sampleFunction->funcEntry(theProc);
1928 sampler = addInstFunc(theProc, func_entry,
1929 ast, callPreInsn, orderLastAtPoint, false);
1932 return true; // success
1935 void sampledIntCounterReqNode::disable(process *theProc,
1936 const vector<unsigVecType> &pointsToCheck) {
1937 // We used to remove the sample id from midToMiMap here but now the caller is
1938 // responsible for that.
1940 // Remove instrumentation added to DYNINSTsampleValues(), if necessary:
1941 if (sampler != NULL)
1942 ::deleteInst(sampler, getAllTrampsAtPoint(sampler));
1944 // Deallocate space for intCounter in the inferior heap:
1945 assert(counterPtr != NULL);
1946 inferiorFree(theProc, (unsigned)counterPtr, dataHeap, pointsToCheck);
1949 void sampledIntCounterReqNode::writeToInferiorHeap(process *theProc,
1950 const intCounter &dataSrc) const {
1951 // using the contents of "dataSrc", write to the inferior heap at loc
1952 // "counterPtr" via proc->writeDataSpace()
1954 theProc->writeDataSpace(counterPtr, sizeof(intCounter), &dataSrc);
1957 bool sampledIntCounterReqNode::
1958 unFork(dictionary_hash<instInstance*,instInstance*> &map) {
1959 instInstance *parentSamplerInstance = this->sampler;
1961 instInstance *childSamplerInstance;
1962 if (!map.find(parentSamplerInstance, childSamplerInstance))
1965 vector<unsigned> pointsToCheck; // empty on purpose
1966 deleteInst(childSamplerInstance, pointsToCheck);
1968 map[parentSamplerInstance] = NULL;
1975 /* ************************************************************************* */
1979 sampledShmIntCounterReqNode::sampledShmIntCounterReqNode(int iValue,
1981 metricDefinitionNode *iMi,
1985 theSampleId = iCounterId;
1986 initialValue = iValue;
1988 // The following fields are NULL until insertInstrumentation()
1989 allocatedIndex = UINT_MAX;
1990 allocatedLevel = UINT_MAX;
1994 if (!computingCost) {
1996 isOk = insertInstrumentation(iMi->proc(), iMi, doNotSample);
2001 sampledShmIntCounterReqNode::
2002 sampledShmIntCounterReqNode(const sampledShmIntCounterReqNode &src,
2003 process *childProc, metricDefinitionNode *mi,
2004 int iCounterId, const process *parentProc) {
2005 // a dup() routine (call after a fork())
2006 // Assumes that "childProc" has been copied already (e.g., the shm seg was copied).
2008 // Note that the index w/in the inferior heap remains the same, so setting the
2009 // new inferiorCounterPtr isn't too hard. Actually, it's trivial, since other code
2010 // ensures that the new shm segment is placed in exactly the same virtual mem location
2011 // as the previous one.
2013 // Note that the fastInferiorHeap class's fork ctor will have already copied the
2014 // actual data; we need to fill in new meta-data (new houseKeeping entries).
2016 this->allocatedIndex = src.allocatedIndex;
2017 this->allocatedLevel = src.allocatedLevel;
2019 this->theSampleId = iCounterId; // this is different from the parent's value
2020 this->initialValue = src.initialValue;
2022 superTable &theTable = childProc->getTable();
2024 // since the new shm seg is placed in exactly the same memory location as
2025 // the old one, nothing here should change.
2026 const superTable &theParentTable = parentProc->getTable();
2027 assert(theTable.index2InferiorAddr(0,childProc->threads[0]->get_pd_pos(),allocatedIndex,allocatedLevel)==theParentTable.index2InferiorAddr(0,parentProc->threads[0]->get_pd_pos(),allocatedIndex,allocatedLevel));
2029 for (unsigned i=0; i<childProc->threads.size(); i++) {
2030 // write to the raw item in the inferior heap:
2031 intCounter *localCounterPtr = (intCounter *) theTable.index2LocalAddr(0,childProc->threads[i]->get_pd_pos(),allocatedIndex,allocatedLevel);
2032 const intCounter *localSrcCounterPtr = (const intCounter *) childProc->getParent()->getTable().index2LocalAddr(0,childProc->threads[i]->get_pd_pos(),allocatedIndex,allocatedLevel);
2033 localCounterPtr->value = initialValue;
2034 localCounterPtr->id.id = theSampleId;
2035 localCounterPtr->theSpinner = localSrcCounterPtr->theSpinner;
2036 // in case we're in the middle of an operation
2039 // write HK for this intCounter:
2040 // Note: we don't assert anything about mi->getMId(), because that id has no
2041 // relation to the ids we work with (theSampleId). In fact, we (the sampling code)
2042 // just don't ever care what mi->getMId() is.
2043 assert(theSampleId >= 0);
2044 assert(midToMiMap.defines(theSampleId));
2045 assert(midToMiMap[theSampleId] == mi);
2046 intCounterHK iHKValue(theSampleId, mi);
2047 // the mi differs from the mi of the parent; theSampleId differs too.
2048 theTable.initializeHKAfterForkIntCounter(allocatedIndex, allocatedLevel, iHKValue);
2054 sampledShmIntCounterReqNode::dup(process *childProc,
2055 metricDefinitionNode *mi,
2057 const dictionary_hash<instInstance*,instInstance*> &
2059 // duplicate 'this' (allocate w/ new) and return. Call after a fork().
2061 sampledShmIntCounterReqNode *tmp;
2062 tmp = new sampledShmIntCounterReqNode(*this, childProc, mi, iCounterId, childProc->getParent());
2068 bool sampledShmIntCounterReqNode::insertInstrumentation(process *theProc,
2069 metricDefinitionNode *iMi, bool doNotSample) {
2070 // Remember counterPtr is NULL until this routine gets called.
2071 // WARNING: there will be an assert failure if the applic hasn't yet attached to the
2074 // initialize the intCounter in the inferior heap
2076 iValue.id.id = this->theSampleId;
2077 iValue.value = this->initialValue; // what about initializing 'theSpinner'???
2079 intCounterHK iHKValue(this->theSampleId, iMi);
2081 superTable &theTable = theProc->getTable();
2083 if (!theTable.allocIntCounter(iValue, iHKValue, this->allocatedIndex, this->allocatedLevel, doNotSample))
2084 return false; // failure
2086 return true; // success
2089 void sampledShmIntCounterReqNode::disable(process *theProc,
2090 const vector<unsigVecType> &pointsToCheck) {
2091 // We used to remove the sample id from midToMiMap here but now the caller is
2092 // responsible for that.
2094 superTable &theTable = theProc->getTable();
2096 // Remove from inferior heap; make sure we won't be sampled any more:
2097 vector<unsigned> trampsMaybeUsing;
2098 for (unsigned pointlcv=0; pointlcv < pointsToCheck.size(); pointlcv++)
2099 for (unsigned tramplcv=0; tramplcv < pointsToCheck[pointlcv].size(); tramplcv++)
2100 trampsMaybeUsing += pointsToCheck[pointlcv][tramplcv];
2102 theTable.makePendingFree(0,allocatedIndex,allocatedLevel,trampsMaybeUsing);
2104 #if defined(MT_THREAD)
2105 //NOTE: Not yet implemented for shm sampling! naim 4/23/97
2106 // pdThread *thr = theProc->threads[0];
2107 // thr->CTvector->remove(this->theSampleId, this->position_);
2108 // theProc->updateActiveCT(false,counter);
2114 /* ************************************************************************* */
2116 nonSampledIntCounterReqNode::nonSampledIntCounterReqNode(int iValue,
2118 metricDefinitionNode *iMi,
2119 bool computingCost) :
2121 theSampleId = iCounterId;
2122 initialValue = iValue;
2124 // The following fields are NULL until insertInstrumentation()
2127 if (!computingCost) {
2129 isOk = insertInstrumentation(iMi->proc(), iMi);
2130 assert(isOk && counterPtr!=NULL);
2134 nonSampledIntCounterReqNode::
2135 nonSampledIntCounterReqNode(const nonSampledIntCounterReqNode &src,
2136 process *childProc, metricDefinitionNode *,
2138 // a dup() routine (call after a fork())
2139 counterPtr = src.counterPtr; // assumes addr spaces have been dup()d.
2140 initialValue = src.initialValue;
2141 theSampleId = iCounterId;
2144 temp.id.id = this->theSampleId;
2145 temp.value = this->initialValue;
2146 writeToInferiorHeap(childProc, temp);
2150 nonSampledIntCounterReqNode::dup(process *childProc,
2151 metricDefinitionNode *mi,
2153 const dictionary_hash<instInstance*,instInstance*> &
2155 // duplicate 'this' (allocate w/ new) and return. Call after a fork().
2157 nonSampledIntCounterReqNode *tmp;
2158 tmp = new nonSampledIntCounterReqNode(*this, childProc, mi, iCounterId);
2164 bool nonSampledIntCounterReqNode::insertInstrumentation(process *theProc,
2165 metricDefinitionNode *,
2167 // Remember counterPtr is NULL until this routine gets called.
2168 counterPtr = (intCounter*)inferiorMalloc(theProc, sizeof(intCounter), dataHeap);
2169 if (counterPtr == NULL)
2170 return false; // failure!
2172 // initialize the intCounter in the inferior heap
2174 temp.id.id = this->theSampleId;
2175 temp.value = this->initialValue;
2177 writeToInferiorHeap(theProc, temp);
2179 return true; // success
2182 void nonSampledIntCounterReqNode::disable(process *theProc,
2183 const vector<unsigVecType> &pointsToCheck) {
2184 // We used to remove the sample id from midToMiMap here but now the caller is
2185 // responsible for that.
2187 // Deallocate space for intCounter in the inferior heap:
2188 assert(counterPtr != NULL);
2189 inferiorFree(theProc, (unsigned)counterPtr, dataHeap, pointsToCheck);
2192 void nonSampledIntCounterReqNode::writeToInferiorHeap(process *theProc,
2193 const intCounter &dataSrc) const {
2194 // using the contents of "dataSrc", write to the inferior heap at loc
2195 // "counterPtr" via proc->writeDataSpace()
2197 theProc->writeDataSpace(counterPtr, sizeof(intCounter), &dataSrc);
2200 /* ****************************************************************** */
2202 #ifndef SHM_SAMPLING
2203 sampledTimerReqNode::sampledTimerReqNode(timerType iType, int iCounterId,
2204 metricDefinitionNode *iMi,
2205 bool computingCost) :
2207 theSampleId = iCounterId;
2208 theTimerType = iType;
2210 // The following fields are NULL until insertInstrumentatoin():
2214 if (!computingCost) {
2216 isOk = insertInstrumentation(iMi->proc(), iMi);
2217 assert(isOk && timerPtr!=NULL);
2221 sampledTimerReqNode::sampledTimerReqNode(const sampledTimerReqNode &src,
2223 metricDefinitionNode *,
2225 const dictionary_hash<instInstance*,instInstance*> &map) {
2226 // a dup()-like routine; call after a fork()
2227 timerPtr = src.timerPtr; // assumes addr spaces have been dup()'d
2229 if (!map.find(src.sampler, this->sampler)) // writes to this->sampler
2232 assert(sampler); // makes sense; timers are always sampled, whereas intCounters
2233 // might be just non-sampled predicates.
2235 theSampleId = iCounterId;
2236 theTimerType = src.theTimerType;
2239 P_memset(&temp, '\0', sizeof(tTimer)); /* is this needed? */
2240 temp.id.id = this->theSampleId;
2241 temp.type = this->theTimerType;
2242 temp.normalize = 1000000;
2243 writeToInferiorHeap(childProc, temp);
2245 // WARNING: shouldn't we be resetting the raw value to count=0, start=0,
2246 // total = src.initialValue ??? On the other hand, it's not that
2247 // simple -- if the timer is active in the parent, then it'll be active
2248 // in the child. So how about setting count to src.count, start=now,
2253 sampledTimerReqNode::dup(process *childProc, metricDefinitionNode *mi,
2255 const dictionary_hash<instInstance*,instInstance*> &map
2257 // duplicate 'this' (allocate w/ new) and return. Call after a fork().
2259 sampledTimerReqNode *result = new sampledTimerReqNode(*this, childProc, mi, iCounterId, map);
2262 return NULL; // on failure, return w/o incrementing counterId
2267 bool sampledTimerReqNode::insertInstrumentation(process *theProc,
2268 metricDefinitionNode *,
2270 timerPtr = (tTimer *)inferiorMalloc(theProc, sizeof(tTimer), dataHeap);
2271 if (timerPtr == NULL)
2272 return false; // failure!
2274 // Now let's initialize the newly allocated tTimer in the inferior heap:
2276 P_memset(&temp, '\0', sizeof(tTimer));
2277 temp.id.id = this->theSampleId;
2278 temp.type = this->theTimerType;
2279 temp.normalize = 1000000;
2280 writeToInferiorHeap(theProc, temp);
2282 // Now instrument DYNINSTreportTimer:
2283 function_base *sampleFunction =
2284 theProc->findOneFunction("DYNINSTsampleValues");
2285 assert(sampleFunction);
2288 tmp = new AstNode(AstNode::Constant, timerPtr);
2289 ast = new AstNode("DYNINSTreportTimer", tmp);
2292 instPoint *func_entry = (instPoint *)sampleFunction->funcEntry(theProc);
2293 sampler = addInstFunc(theProc, func_entry, ast,
2294 callPreInsn, orderLastAtPoint, false);
2297 return true; // successful
2300 void sampledTimerReqNode::disable(process *theProc,
2301 const vector<unsigVecType> &pointsToCheck) {
2302 // We used to remove the sample id from midToMiMap here but now the caller is
2303 // responsible for that.
2305 // Remove instrumentation added to DYNINSTsampleValues(), if necessary:
2306 if (sampler != NULL)
2307 ::deleteInst(sampler, getAllTrampsAtPoint(sampler));
2309 // Deallocate space for tTimer in the inferior heap:
2311 inferiorFree(theProc, (unsigned)timerPtr, dataHeap, pointsToCheck);
2314 void sampledTimerReqNode::writeToInferiorHeap(process *theProc,
2315 const tTimer &dataSrc) const {
2316 // using contents of "dataSrc", a local copy of the data,
2317 // write to inferior heap at loc "timerPtr" via proc->writeDataSpace()
2319 theProc->writeDataSpace(timerPtr, sizeof(tTimer), &dataSrc);
2322 bool sampledTimerReqNode::
2323 unFork(dictionary_hash<instInstance*,instInstance*> &map) {
2324 instInstance *parentSamplerInstance = sampler;
2326 instInstance *childSamplerInstance;
2327 if (!map.find(parentSamplerInstance, childSamplerInstance))
2330 vector<unsigned> pointsToCheck; // empty
2331 deleteInst(childSamplerInstance, pointsToCheck);
2333 map[parentSamplerInstance] = NULL; // since we've deleted...
2340 /* ****************************************************************** */
2343 sampledShmWallTimerReqNode::sampledShmWallTimerReqNode(int iCounterId,
2344 metricDefinitionNode *iMi,
2345 bool computingCost) :
2347 theSampleId = iCounterId;
2349 // The following fields are NULL until insertInstrumentation():
2350 allocatedIndex = UINT_MAX;
2351 allocatedLevel = UINT_MAX;
2355 if (!computingCost) {
2357 isOk = insertInstrumentation(iMi->proc(), iMi);
2362 sampledShmWallTimerReqNode::
2363 sampledShmWallTimerReqNode(const sampledShmWallTimerReqNode &src,
2365 metricDefinitionNode *mi,
2366 int iCounterId, const process *parentProc) {
2367 // a dup()-like routine; call after a fork().
2368 // Assumes that the "childProc" has been duplicated already
2370 // Note that the index w/in the inferior heap remains the same, so setting the new
2371 // inferiorTimerPtr isn't too hard. Actually, it's trivial, since other code
2372 // ensures that the new shm segment is placed in exactly the same virtual mem loc
2373 // as the previous one.
2375 // Note that the fastInferiorHeap class's fork ctor will have already copied the
2376 // actual data; we need to fill in new meta-data (new houseKeeping entries).
2378 allocatedIndex = src.allocatedIndex;
2379 allocatedLevel = src.allocatedLevel;
2381 theSampleId = iCounterId;
2382 assert(theSampleId != src.theSampleId);
2384 superTable &theTable = childProc->getTable();
2386 // since the new shm seg is placed in exactly the same memory location as
2387 // the old one, nothing here should change.
2388 const superTable &theParentTable = parentProc->getTable();
2389 assert(theTable.index2InferiorAddr(1,childProc->threads[0]->get_pd_pos(),allocatedIndex,allocatedLevel)==theParentTable.index2InferiorAddr(1,parentProc->threads[0]->get_pd_pos(),allocatedIndex,allocatedLevel));
2391 // Write new raw value in the inferior heap:
2392 // we set localTimerPtr as follows: protector1 and procetor2 should be copied from
2393 // src. total should be reset to 0. start should be set to now if active else 0.
2394 // counter should be copied from the source.
2395 // NOTE: SINCE WE COPY FROM THE SOURCE, IT'S IMPORTANT THAT ON A FORK, BOTH THE
2396 // PARENT AND CHILD ARE PAUSED UNTIL WE COPY THINGS OVER. THAT THE CHILD IS
2397 // PAUSED IS NOTHING NEW; THAT THE PARENT SHOULD BE PAUSED IS NEW NEWS!
2399 for (unsigned i=0; i<childProc->threads.size(); i++) {
2400 tTimer *localTimerPtr = (tTimer *) theTable.index2LocalAddr(1,childProc->threads[i]->get_pd_pos(),allocatedIndex,allocatedLevel);
2401 const tTimer *srcTimerPtr = (const tTimer *) childProc->getParent()->getTable().index2LocalAddr(1,childProc->threads[i]->get_pd_pos(),allocatedIndex,allocatedLevel);
2403 localTimerPtr->total = 0;
2404 localTimerPtr->counter = srcTimerPtr->counter;
2405 localTimerPtr->id.id = theSampleId;
2406 localTimerPtr->protector1 = srcTimerPtr->protector1;
2407 localTimerPtr->protector2 = srcTimerPtr->protector2;
2409 if (localTimerPtr->counter == 0)
2410 // inactive timer...this is the easy case to copy
2411 localTimerPtr->start = 0; // undefined, really
2413 // active timer...don't copy the start time from the source...make it 'now'
2414 localTimerPtr->start = getCurrWallTime();
2417 // write new HK for this tTimer:
2418 // Note: we don't assert anything about mi->getMId(), because that id has no
2419 // relation to the ids we work with (theSampleId). In fact, we (the sampling code)
2420 // just don't ever care what mi->getMId() is.
2421 assert(theSampleId >= 0);
2422 assert(midToMiMap.defines(theSampleId));
2423 assert(midToMiMap[theSampleId] == mi);
2424 wallTimerHK iHKValue(theSampleId, mi, 0); // is last param right?
2425 // the mi should differ from the mi of the parent; theSampleId differs too.
2426 theTable.initializeHKAfterForkWallTimer(allocatedIndex, allocatedLevel, iHKValue);
2432 sampledShmWallTimerReqNode::dup(process *childProc,
2433 metricDefinitionNode *mi,
2435 const dictionary_hash<instInstance*,instInstance*> &
2437 // duplicate 'this' (allocate w/ new) and return. Call after a fork().
2439 sampledShmWallTimerReqNode *tmp;
2440 tmp = new sampledShmWallTimerReqNode(*this, childProc, mi, iCounterId, childProc->getParent());
2446 bool sampledShmWallTimerReqNode::insertInstrumentation(process *theProc,
2447 metricDefinitionNode *iMi, bool) {
2448 // Remember inferiorTimerPtr is NULL until this routine gets called.
2449 // WARNING: there will be an assert failure if the applic hasn't yet attached to the
2452 // initialize the tTimer in the inferior heap
2454 P_memset(&iValue, '\0', sizeof(tTimer));
2455 iValue.id.id = this->theSampleId;
2457 wallTimerHK iHKValue(this->theSampleId, iMi, 0);
2459 superTable &theTable = theProc->getTable();
2461 if (!theTable.allocWallTimer(iValue, iHKValue, this->allocatedIndex, this->allocatedLevel))
2462 return false; // failure
2467 void sampledShmWallTimerReqNode::disable(process *theProc,
2468 const vector<unsigVecType> &pointsToCheck) {
2469 // We used to remove the sample id from midToMiMap here but now the caller is
2470 // responsible for that.
2472 superTable &theTable = theProc->getTable();
2474 // Remove from inferior heap; make sure we won't be sampled any more:
2475 vector<unsigned> trampsMaybeUsing;
2476 for (unsigned pointlcv=0; pointlcv < pointsToCheck.size(); pointlcv++)
2477 for (unsigned tramplcv=0; tramplcv < pointsToCheck[pointlcv].size(); tramplcv++)
2478 trampsMaybeUsing += pointsToCheck[pointlcv][tramplcv];
2480 theTable.makePendingFree(1,allocatedIndex,allocatedLevel,trampsMaybeUsing);
2482 #if defined(MT_THREAD)
2483 //NOTE: Not yet implemented for shm sampling! naim 4/23/97
2484 // pdThread *thr = theProc->threads[0];
2485 // thr->CTvector->remove(this->theSampleId, this->position_);
2486 // theProc->updateActiveCT(false,wallTimer);
2490 /* ****************************************************************** */
2492 sampledShmProcTimerReqNode::sampledShmProcTimerReqNode(int iCounterId,
2493 metricDefinitionNode *iMi,
2494 bool computingCost) :
2496 theSampleId = iCounterId;
2498 // The following fields are NULL until insertInstrumentatoin():
2499 allocatedIndex = UINT_MAX;
2500 allocatedLevel = UINT_MAX;
2504 if (!computingCost) {
2506 isOk = insertInstrumentation(iMi->proc(), iMi);
2511 sampledShmProcTimerReqNode::
2512 sampledShmProcTimerReqNode(const sampledShmProcTimerReqNode &src,
2514 metricDefinitionNode *mi,
2515 int iCounterId, const process *parentProc) {
2516 // a dup()-like routine; call after a fork()
2517 // Assumes that the "childProc" has been duplicated already
2519 // Note that the index w/in the inferior heap remains the same, so setting the new
2520 // inferiorTimerPtr isn't too hard. Actually, it's trivial, since other code
2521 // ensures that the new shm segment is placed in exactly the same virtual mem loc
2522 // as the previous one.
2524 // Note that the fastInferiorHeap class's fork ctor will have already copied the
2525 // actual data; we need to fill in new meta-data (new houseKeeping entries).
2527 allocatedIndex = src.allocatedIndex;
2528 theSampleId = iCounterId;
2529 assert(theSampleId != src.theSampleId);
2531 superTable &theTable = childProc->getTable();
2533 // since the new shm seg is placed in exactly the same memory location as
2534 // the old one, nothing here should change.
2535 const superTable &theParentTable = parentProc->getTable();
2536 assert(theTable.index2InferiorAddr(2,childProc->threads[0]->get_pd_pos(),allocatedIndex,allocatedLevel)==theParentTable.index2InferiorAddr(2,parentProc->threads[0]->get_pd_pos(),allocatedIndex,allocatedLevel));
2538 // Write new raw value:
2539 // we set localTimerPtr as follows: protector1 and procetor2 should be copied from
2540 // src. total should be reset to 0. start should be set to now if active else 0.
2541 // counter should be copied from the source.
2542 // NOTE: SINCE WE COPY FROM THE SOURCE, IT'S IMPORTANT THAT ON A FORK, BOTH THE
2543 // PARENT AND CHILD ARE PAUSED UNTIL WE COPY THINGS OVER. THAT THE CHILD IS
2544 // PAUSED IS NOTHING NEW; THAT THE PARENT SHOULD BE PAUSED IS NEW NEWS!
2546 for (unsigned i=0; i<childProc->threads.size(); i++) {
2547 tTimer *localTimerPtr = (tTimer *) theTable.index2LocalAddr(2,childProc->threads[i]->get_pd_pos(),allocatedIndex,allocatedLevel);
2548 const tTimer *srcTimerPtr = (const tTimer *) childProc->getParent()->getTable().index2LocalAddr(2,childProc->threads[i]->get_pd_pos(),allocatedIndex,allocatedLevel);
2550 localTimerPtr->total = 0;
2551 localTimerPtr->counter = srcTimerPtr->counter;
2552 localTimerPtr->id.id = theSampleId;
2553 localTimerPtr->protector1 = srcTimerPtr->protector1;
2554 localTimerPtr->protector2 = srcTimerPtr->protector2;
2556 if (localTimerPtr->counter == 0)
2557 // inactive timer...this is the easy case to copy
2558 localTimerPtr->start = 0; // undefined, really
2560 // active timer...don't copy the start time from the source...make it 'now'
2561 localTimerPtr->start = childProc->getInferiorProcessCPUtime();
2564 // Write new HK for this tTimer:
2565 // Note: we don't assert anything about mi->getMId(), because that id has no
2566 // relation to the ids we work with (theSampleId). In fact, we (the sampling code)
2567 // just don't ever care what mi->getMId() is.
2568 assert(theSampleId >= 0);
2569 assert(midToMiMap.defines(theSampleId));
2570 assert(midToMiMap[theSampleId] == mi);
2571 processTimerHK iHKValue(theSampleId, mi, 0); // is last param right?
2572 // the mi differs from the mi of the parent; theSampleId differs too.
2573 theTable.initializeHKAfterForkProcTimer(allocatedIndex, allocatedLevel, iHKValue);
2579 sampledShmProcTimerReqNode::dup(process *childProc,
2580 metricDefinitionNode *mi,
2582 const dictionary_hash<instInstance*,instInstance*> &
2584 // duplicate 'this' (allocate w/ new) and return. Call after a fork().
2586 sampledShmProcTimerReqNode *tmp;
2587 tmp = new sampledShmProcTimerReqNode(*this, childProc, mi, iCounterId, childProc->getParent());
2593 bool sampledShmProcTimerReqNode::insertInstrumentation(process *theProc,
2594 metricDefinitionNode *iMi, bool) {
2595 // Remember inferiorTimerPtr is NULL until this routine gets called.
2596 // WARNING: there will be an assert failure if the applic hasn't yet attached to the
2599 // initialize the tTimer in the inferior heap
2601 P_memset(&iValue, '\0', sizeof(tTimer));
2602 iValue.id.id = this->theSampleId;
2604 processTimerHK iHKValue(this->theSampleId, iMi, 0);
2606 superTable &theTable = theProc->getTable();
2608 if (!theTable.allocProcTimer(iValue, iHKValue, this->allocatedIndex,this->allocatedLevel))
2609 return false; // failure
2614 void sampledShmProcTimerReqNode::disable(process *theProc,
2615 const vector<unsigVecType> &pointsToCheck) {
2616 // We used to remove the sample id from midToMiMap here but now the caller is
2617 // responsible for that.
2619 superTable &theTable = theProc->getTable();
2621 // Remove from inferior heap; make sure we won't be sampled any more:
2622 vector<unsigned> trampsMaybeUsing;
2623 for (unsigned pointlcv=0; pointlcv < pointsToCheck.size(); pointlcv++)
2624 for (unsigned tramplcv=0; tramplcv < pointsToCheck[pointlcv].size(); tramplcv++)
2625 trampsMaybeUsing += pointsToCheck[pointlcv][tramplcv];
2627 theTable.makePendingFree(2,allocatedIndex,allocatedLevel,trampsMaybeUsing);
2629 #if defined(MT_THREAD)
2630 //NOTE: Not yet implemented for shm sampling! naim 4/23/97
2631 // pdThread *thr = theProc->threads[0];
2632 // thr->CTvector->remove(this->theSampleId, this->position_);
2633 // theProc->updateActiveCT(false,procTimer);
2638 /* **************************** */
2640 void reportInternalMetrics(bool force)
2642 if (isApplicationPaused())
2643 return; // we don't sample when paused (is this right?)
2645 static timeStamp end=0.0;
2647 // see if we have a sample to establish time base.
2648 if (!firstRecordTime) {
2649 //cerr << "reportInternalMetrics: no because firstRecordTime==0" << endl;
2654 end = (timeStamp)firstRecordTime/MILLION;
2656 const timeStamp now = getCurrentTime(false);
2658 // check if it is time for a sample
2659 if (!force && now < end + samplingRate) {
2660 // cerr << "reportInternalMetrics: no because now < end + samplingRate (end=" << end << "; samplingRate=" << samplingRate << "; now=" << now << ")" << endl;
2661 // cerr << "difference is " << (end+samplingRate-now) << endl;
2665 timeStamp start = end;
2668 // TODO -- clean me up, please
2673 for (unsigned u1 = 0; u1 < processVec.size(); u1++) {
2674 if (processVec[u1]->numOfActCounters_is > max1)
2675 max1=processVec[u1]->numOfActCounters_is;
2676 if (processVec[u1]->numOfActProcTimers_is > max2)
2677 max2=processVec[u1]->numOfActProcTimers_is;
2678 if (processVec[u1]->numOfActWallTimers_is > max3)
2679 max3=processVec[u1]->numOfActWallTimers_is;
2681 numOfActCounters_all=max1;
2682 numOfActProcTimers_all=max2;
2683 numOfActWallTimers_all=max3;
2685 unsigned ai_size = internalMetric::allInternalMetrics.size();
2686 for (unsigned u2=0; u2<ai_size; u2++) {
2687 internalMetric *theIMetric = internalMetric::allInternalMetrics[u2];
2688 // Loop thru all enabled instances of this internal metric...
2690 for (unsigned v=0; v < theIMetric->num_enabled_instances(); v++) {
2691 internalMetric::eachInstance &theInstance = theIMetric->getEnabledInstance(v);
2692 // not "const" since bumpCumulativeValueBy() may be called
2694 sampleValue value = (sampleValue) 0;
2695 if (theIMetric->name() == "active_processes") {
2696 //value = (end - start) * activeProcesses;
2697 value = (end - start) * theInstance.getValue();
2698 } else if (theIMetric->name() == "bucket_width") {
2699 //value = (end - start)* theInstance.getValue();
2700 // I would prefer to use (end-start) * theInstance.getValue(); however,
2701 // we've had some problems getting setValue() called in time, thus
2702 // leaving us with getValues() of 0 sometimes. See longer comment in dynrpc.C --ari
2703 extern float currSamplingRate;
2704 value = (end - start) * currSamplingRate;
2705 } else if (theIMetric->name() == "number_of_cpus") {
2706 value = (end - start) * numberOfCPUs;
2707 } else if (theIMetric->name() == "total_CT") {
2708 value = (end - start) * internalMetricCounterId;
2710 } else if (theIMetric->name() == "numOfActCounters") {
2711 value = (end - start) * numOfActCounters_all;
2713 } else if (theIMetric->name() == "numOfActProcTimers") {
2714 value = (end - start) * numOfActProcTimers_all;
2716 } else if (theIMetric->name() == "numOfActWallTimers") {
2717 value = (end - start) * numOfActWallTimers_all;
2719 } else if (theIMetric->name() == "active_CT") {
2720 value = (end - start) * (numOfActCounters_all+numOfActProcTimers_all+numOfActWallTimers_all);
2722 } else if (theIMetric->name() == "infHeapMemAvailable") {
2723 value = (end - start) * inferiorMemAvailable;
2725 } else if (theIMetric->style() == EventCounter) {
2726 value = theInstance.getValue();
2727 // assert((value + 0.0001) >= imp->cumulativeValue);
2728 value -= theInstance.getCumulativeValue();
2729 theInstance.bumpCumulativeValueBy(value);
2730 } else if (theIMetric->style() == SampledFunction) {
2731 value = theInstance.getValue();
2734 theInstance.report(start, end, value);
2735 // calls metricDefinitionNode->forwardSimpleValue()
2740 void disableAllInternalMetrics() {
2741 for (unsigned u=0; u < internalMetric::allInternalMetrics.size(); u++) {
2742 internalMetric *theIMetric = internalMetric::allInternalMetrics[u];
2744 // Now loop thru all the enabled instances of this internal metric...
2745 while (theIMetric->num_enabled_instances() > 0) {
2746 internalMetric::eachInstance &theInstance = theIMetric->getEnabledInstance(0);
2747 tp->endOfDataCollection(theInstance.getMId());
2748 theIMetric->disableInstance(0);
2755 unsigned sampledShmIntCounterReqNode::getInferiorPtr(process *proc) const {
2756 // counterPtr could be NULL if we are building AstNodes just to compute
2757 // the cost - naim 2/18/97
2759 // this routine will dissapear because we can't compute the address
2760 // of the counter/timer without knowing the thread id - naim 3/17/97
2762 if (allocatedIndex == UINT_MAX || allocatedLevel == UINT_MAX) return(0);
2763 assert(proc != NULL);
2764 superTable &theTable = proc->getTable();
2765 // we assume there is only one thread
2766 return((unsigned) theTable.index2InferiorAddr(0,0,allocatedIndex,allocatedLevel));
2769 unsigned sampledShmWallTimerReqNode::getInferiorPtr(process *proc) const {
2770 // counterPtr could be NULL if we are building AstNodes just to compute
2771 // the cost - naim 2/18/97
2773 // this routine will dissapear because we can't compute the address
2774 // of the counter/timer without knowing the thread id - naim 3/17/97
2776 if (allocatedIndex == UINT_MAX || allocatedLevel == UINT_MAX) return(0);
2777 assert(proc != NULL);
2778 superTable &theTable = proc->getTable();
2779 // we assume there is only one thread
2780 return((unsigned) theTable.index2InferiorAddr(1,0,allocatedIndex,allocatedLevel));
2783 unsigned sampledShmProcTimerReqNode::getInferiorPtr(process *proc) const {
2784 // counterPtr could be NULL if we are building AstNodes just to compute
2785 // the cost - naim 2/18/97
2787 // this routine will dissapear because we can't compute the address
2788 // of the counter/timer without knowing the thread id - naim 3/17/97
2790 if (allocatedIndex == UINT_MAX || allocatedLevel == UINT_MAX) return(0);
2791 assert(proc != NULL);
2792 superTable &theTable = proc->getTable();
2793 // we assume there is only one thread
2794 return((unsigned) theTable.index2InferiorAddr(2,0,allocatedIndex,allocatedLevel));