HPCToolkit
ParallelAnalysis.cpp
Go to the documentation of this file.
1 // -*-Mode: C++;-*-
2 
3 // * BeginRiceCopyright *****************************************************
4 //
5 // $HeadURL$
6 // $Id$
7 //
8 // --------------------------------------------------------------------------
9 // Part of HPCToolkit (hpctoolkit.org)
10 //
11 // Information about sources of support for research and development of
12 // HPCToolkit is at 'hpctoolkit.org' and in 'README.Acknowledgments'.
13 // --------------------------------------------------------------------------
14 //
15 // Copyright ((c)) 2002-2019, Rice University
16 // All rights reserved.
17 //
18 // Redistribution and use in source and binary forms, with or without
19 // modification, are permitted provided that the following conditions are
20 // met:
21 //
22 // * Redistributions of source code must retain the above copyright
23 // notice, this list of conditions and the following disclaimer.
24 //
25 // * Redistributions in binary form must reproduce the above copyright
26 // notice, this list of conditions and the following disclaimer in the
27 // documentation and/or other materials provided with the distribution.
28 //
29 // * Neither the name of Rice University (RICE) nor the names of its
30 // contributors may be used to endorse or promote products derived from
31 // this software without specific prior written permission.
32 //
33 // This software is provided by RICE and contributors "as is" and any
34 // express or implied warranties, including, but not limited to, the
35 // implied warranties of merchantability and fitness for a particular
36 // purpose are disclaimed. In no event shall RICE or contributors be
37 // liable for any direct, indirect, incidental, special, exemplary, or
38 // consequential damages (including, but not limited to, procurement of
39 // substitute goods or services; loss of use, data, or profits; or
40 // business interruption) however caused and on any theory of liability,
41 // whether in contract, strict liability, or tort (including negligence
42 // or otherwise) arising in any way out of the use of this software, even
43 // if advised of the possibility of such damage.
44 //
45 // ******************************************************* EndRiceCopyright *
46 
47 //***************************************************************************
48 //
49 // File:
50 // $HeadURL$
51 //
52 // Purpose:
53 // [The purpose of this file]
54 //
55 // Description:
56 // [The set of functions, macros, etc. defined in the file]
57 //
58 //***************************************************************************
59 
60 //**************************** MPI Include Files ****************************
61 
62 #include <mpi.h>
63 
64 //************************* System Include Files ****************************
65 
66 #include <iostream>
67 
68 #include <string>
69 using std::string;
70 
71 #include <algorithm>
72 
73 #include <stdint.h>
74 
75 //*************************** User Include Files ****************************
76 
77 #include <include/uint.h>
78 
79 #include "ParallelAnalysis.hpp"
80 
82 #include <lib/analysis/Util.hpp>
83 
85 #include <lib/support/StrUtil.hpp>
86 
87 
88 //*************************** Forward Declarations **************************
89 
90 #define DBG_CCT_MERGE 0
91 #define DBG_PARALLEL_ANALYSIS 0
92 
93 //***************************************************************************
94 
95 
96 namespace ParallelAnalysis {
97 
98 //***************************************************************************
99 // forward declarations
100 //***************************************************************************
101 
102 static void
103 packStringSet(const StringSet& profile,
104  uint8_t** buffer, size_t* bufferSz);
105 
106 static StringSet*
107 unpackStringSet(uint8_t* buffer, size_t bufferSz);
108 
109 //***************************************************************************
110 // private functions
111 //***************************************************************************
112 
113 static void
115 (
116  size_t &size,
117  MPI_Comm comm
118 )
119 {
120  long size_l = size;
121  MPI_Bcast(&size_l, 1, MPI_LONG, 0, comm);
122  size = size_l;
123 }
124 
125 
126 
127 //***************************************************************************
128 // interface functions
129 //***************************************************************************
130 
131 void
132 broadcast
133 (
134  Prof::CallPath::Profile*& profile,
135  int myRank,
136  MPI_Comm comm
137 )
138 {
139  size_t size = 0;
140  uint8_t* buf = NULL;
141 
142  if (myRank == 0) {
143  packProfile(*profile, &buf, &size);
144  }
145 
146  broadcast_sizet(size, comm);
147 
148  if (myRank != 0) {
149  buf = (uint8_t *)malloc(size * sizeof(uint8_t));
150  }
151 
152  DIAG_DevMsgIf(DBG_PARALLEL_ANALYSIS, myRank << ": broadcast " << size << " bytes" << std::endl);
153  MPI_Bcast(buf, size, MPI_BYTE, 0, comm);
154 
155  if (myRank != 0) {
156  profile = unpackProfile(buf, size);
157  }
158 
159  free(buf);
160 }
161 
162 void
163 broadcast
164 (
165  StringSet &stringSet,
166  int myRank,
167  MPI_Comm comm
168 )
169 {
170  size_t size = 0;
171  uint8_t* buf = NULL;
172 
173  if (myRank == 0) {
174  packStringSet(stringSet, &buf, &size);
175  }
176 
177  broadcast_sizet(size, comm);
178 
179  if (myRank != 0) {
180  buf = (uint8_t *)malloc(size * sizeof(uint8_t));
181  }
182 
183  MPI_Bcast(buf, size, MPI_BYTE, 0, comm);
184 
185  if (myRank != 0) {
186  StringSet *rhs = unpackStringSet(buf, size);
187  stringSet += *rhs;
188  delete rhs;
189  }
190 
191  free(buf);
192 }
193 
194 
195 void
197  int dest, int myRank, MPI_Comm comm)
198 {
199  uint8_t* profileBuf = NULL;
200  size_t profileBufSz = 0;
201  packProfile(*profile, &profileBuf, &profileBufSz);
202  MPI_Send(profileBuf, (int)profileBufSz, MPI_BYTE, dest, myRank, comm);
203  free(profileBuf);
204 }
205 
206 void
208  int src, int myRank, MPI_Comm comm)
209 {
210  // probe src
211  MPI_Status mpistat;
212  MPI_Probe(src, src, comm, &mpistat);
213  int profileBufSz;
214  MPI_Get_count(&mpistat, MPI_BYTE, &profileBufSz);
215 
216  // receive profile from src
217  uint8_t *profileBuf = new uint8_t[profileBufSz];
218  MPI_Recv(profileBuf, profileBufSz, MPI_BYTE, src, src, comm, &mpistat);
219  Prof::CallPath::Profile* new_profile =
220  unpackProfile(profileBuf, (size_t)profileBufSz);
221  delete[] profileBuf;
222 
223  if (DBG_CCT_MERGE) {
224  string pfx0 = "[" + StrUtil::toStr(myRank) + "]";
225  string pfx1 = "[" + StrUtil::toStr(src) + "]";
226  DIAG_DevMsgIf(1, profile->metricMgr()->toString(pfx0.c_str()));
227  DIAG_DevMsgIf(1, new_profile->metricMgr()->toString(pfx1.c_str()));
228  }
229 
231  profile->merge(*new_profile, mergeTy);
232 
233  // merging the perf event statistics
234  profile->metricMgr()->mergePerfEventStatistics(new_profile->metricMgr());
235 
236  if (DBG_CCT_MERGE) {
237  string pfx = ("[" + StrUtil::toStr(src)
238  + " => " + StrUtil::toStr(myRank) + "]");
239  DIAG_DevMsgIf(1, profile->metricMgr()->toString(pfx.c_str()));
240  }
241 
242  delete new_profile;
243 }
244 
245 void
248  int dest, int myRank, MPI_Comm comm)
249 {
250  Prof::CallPath::Profile* profile = data.first;
251  ParallelAnalysis::PackedMetrics* packedMetrics = data.second;
252  packMetrics(*profile, *packedMetrics);
253  MPI_Send(packedMetrics->data(), packedMetrics->dataSize(),
254  MPI_DOUBLE, dest, myRank, comm);
255 }
256 
257 void
260  int src, int myRank, MPI_Comm comm)
261 {
262  Prof::CallPath::Profile* profile = data.first;
263  ParallelAnalysis::PackedMetrics* packedMetrics = data.second;
264 
265  // receive new metric data from src
266  MPI_Status mpistat;
267  MPI_Recv(packedMetrics->data(), packedMetrics->dataSize(),
268  MPI_DOUBLE, src, src, comm, &mpistat);
269  DIAG_Assert(packedMetrics->verify(), DIAG_UnexpectedInput);
270  unpackMetrics(*profile, *packedMetrics);
271 }
272 
273 void
274 packSend(StringSet *stringSet,
275  int dest, int myRank, MPI_Comm comm)
276 {
277  uint8_t* stringSetBuf = NULL;
278  size_t stringSetBufSz = 0;
279  packStringSet(*stringSet, &stringSetBuf, &stringSetBufSz);
280  MPI_Send(stringSetBuf, (int)stringSetBufSz, MPI_BYTE,
281  dest, myRank, comm);
282  free(stringSetBuf);
283 }
284 
285 void
286 recvMerge(StringSet *stringSet,
287  int src, int myRank, MPI_Comm comm)
288 {
289  // determine size of incoming packed directory set from src
290  MPI_Status mpistat;
291  MPI_Probe(src, src, comm, &mpistat);
292  int stringSetBufSz = 0;
293  MPI_Get_count(&mpistat, MPI_BYTE, &stringSetBufSz);
294 
295  // receive new stringSet from src
296  uint8_t *stringSetBuf = new uint8_t[stringSetBufSz];
297  MPI_Recv(stringSetBuf, stringSetBufSz, MPI_BYTE,
298  src, src, comm, &mpistat);
299  StringSet *new_stringSet =
300  unpackStringSet(stringSetBuf, (size_t) stringSetBufSz);
301  delete[] stringSetBuf;
302  *stringSet += *new_stringSet;
303  delete new_stringSet;
304 }
305 
306 
307 //***************************************************************************
308 
309 void
311  uint8_t** buffer, size_t* bufferSz)
312 {
313  // open_memstream: mallocs buffer and sets bufferSz
314  FILE* fs = open_memstream((char**)buffer, bufferSz);
315 
317  Prof::CallPath::Profile::fmt_fwrite(profile, fs, wFlags);
318 
319  fclose(fs);
320 }
321 
322 
324 unpackProfile(uint8_t* buffer, size_t bufferSz)
325 {
326  FILE* fs = fmemopen(buffer, bufferSz, "r");
327 
330  Prof::CallPath::Profile::fmt_fread(prof, fs, rFlags,
331  "(ParallelAnalysis::unpackProfile)",
332  NULL, NULL);
333 
334  fclose(fs);
335  return prof;
336 }
337 
338 
339 
340 //***************************************************************************
341 
342 static void
343 packStringSet(const StringSet& stringSet,
344  uint8_t** buffer, size_t* bufferSz)
345 {
346  // open_memstream: malloc buffer and sets bufferSz
347  FILE* fs = open_memstream((char**)buffer, bufferSz);
348 
349  StringSet::fmt_fwrite(stringSet, fs);
350 
351  fclose(fs);
352 }
353 
354 
355 static StringSet*
356 unpackStringSet(uint8_t* buffer, size_t bufferSz)
357 {
358  FILE* fs = fmemopen(buffer, bufferSz, "r");
359 
360  StringSet* stringSet = NULL;
361 
362  StringSet::fmt_fread(stringSet, fs);
363 
364  fclose(fs);
365 
366  return stringSet;
367 }
368 
369 
370 //***************************************************************************
371 
372 void
374  ParallelAnalysis::PackedMetrics& packedMetrics)
375 {
376  Prof::CCT::Tree& cct = *profile.cct();
377 
378  // pack derived metrics [mDrvdBeg, mDrvdEnd) from 'profile' into
379  // 'packedMetrics'
380  uint mDrvdBeg = packedMetrics.mDrvdBegId();
381  uint mDrvdEnd = packedMetrics.mDrvdEndId();
382 
383  DIAG_Assert(packedMetrics.numNodes() == cct.maxDenseId() + 1, "");
384  DIAG_Assert(packedMetrics.numMetrics() == mDrvdEnd - mDrvdBeg, "");
385 
386  for (Prof::CCT::ANodeIterator it(cct.root()); it.Current(); ++it) {
387  Prof::CCT::ANode* n = it.current();
388  for (uint mId1 = 0, mId2 = mDrvdBeg; mId2 < mDrvdEnd; ++mId1, ++mId2) {
389  packedMetrics.idx(n->id(), mId1) = n->metric(mId2);
390  }
391  }
392 }
393 
394 
395 void
397  const ParallelAnalysis::PackedMetrics& packedMetrics)
398 {
399  Prof::CCT::Tree& cct = *profile.cct();
400 
401  // 1. unpack 'packedMetrics' into temporary derived metrics [mBegId,
402  // mEndId) in 'profile'
403  uint mBegId = packedMetrics.mBegId(), mEndId = packedMetrics.mEndId();
404 
405  DIAG_Assert(packedMetrics.numNodes() == cct.maxDenseId() + 1, "");
406  DIAG_Assert(packedMetrics.numMetrics() == mEndId - mBegId, "");
407 
408  for (uint nodeId = 1; nodeId < packedMetrics.numNodes(); ++nodeId) {
409  for (uint mId1 = 0, mId2 = mBegId; mId2 < mEndId; ++mId1, ++mId2) {
410  Prof::CCT::ANode* n = cct.findNode(nodeId);
411  n->demandMetric(mId2) = packedMetrics.idx(nodeId, mId1);
412  }
413  }
414 
415  // 2. update derived metrics [mDrvdBeg, mDrvdEnd) based on new
416  // values in [mBegId, mEndId)
417  uint mDrvdBeg = packedMetrics.mDrvdBegId();
418  uint mDrvdEnd = packedMetrics.mDrvdEndId();
419  cct.root()->computeMetricsIncr(*profile.metricMgr(), mDrvdBeg, mDrvdEnd,
421 }
422 
423 
424 
425 //***************************************************************************
426 
427 } // namespace ParallelAnalysis
static void packStringSet(const StringSet &profile, uint8_t **buffer, size_t *bufferSz)
#define DBG_PARALLEL_ANALYSIS
void MONITOR_EXT_WRAP_NAME() free(void *ptr)
#define DBG_CCT_MERGE
static int fmt_fread(StringSet *&stringSet, FILE *infs)
Definition: StringSet.cpp:84
void mergePerfEventStatistics(Mgr *source)
Definition: Metric-Mgr.cpp:203
string toStr(const int x, int base)
Definition: StrUtil.cpp:243
ANode * root() const
Definition: CCT-Tree.hpp:160
static int fmt_fwrite(const StringSet &stringSet, FILE *outfs)
Definition: StringSet.cpp:122
ANode * findNode(uint nodeId) const
Definition: CCT-Tree.cpp:243
double metric(size_t mId) const
Definition: fmt.c:108
static StringSet * unpackStringSet(uint8_t *buffer, size_t bufferSz)
void recvMerge(Prof::CallPath::Profile *profile, int src, int myRank, MPI_Comm comm)
std::string toString(const char *pfx="") const
Definition: Metric-Mgr.cpp:735
unsigned int uint
Definition: uint.h:124
void unpackMetrics(Prof::CallPath::Profile &profile, const ParallelAnalysis::PackedMetrics &packedMetrics)
static void broadcast_sizet(size_t &size, MPI_Comm comm)
void computeMetricsIncr(const Metric::Mgr &mMgr, uint mBegId, uint mEndId, Metric::AExprIncr::FnTy fn)
Definition: CCT-Tree.cpp:644
uint merge(Profile &y, int mergeTy, uint mrgFlag=0)
uint maxDenseId() const
Definition: CCT-Tree.hpp:193
void broadcast(Prof::CallPath::Profile *&profile, int myRank, MPI_Comm comm)
static int fmt_fwrite(const Profile &prof, FILE *outfs, uint wFlags)
Prof::CallPath::Profile * unpackProfile(uint8_t *buffer, size_t bufferSz)
const Metric::Mgr * metricMgr() const
const char * DIAG_UnexpectedInput
void packSend(Prof::CallPath::Profile *profile, int dest, int myRank, MPI_Comm comm)
double demandMetric(size_t mId, size_t size=0) const
void *MONITOR_EXT_WRAP_NAME() malloc(size_t bytes)
#define NULL
Definition: ElfHelper.cpp:85
void packProfile(const Prof::CallPath::Profile &profile, uint8_t **buffer, size_t *bufferSz)
static int fmt_fread(Profile *&prof, FILE *infs, uint rFlags, std::string ctxtStr, const char *filename, FILE *outfs)
double idx(uint idxNodes, uint idxMetrics) const
uint id() const
Definition: CCT-Tree.hpp:383
void packMetrics(const Prof::CallPath::Profile &profile, ParallelAnalysis::PackedMetrics &packedMetrics)
<!-- ********************************************************************--> n<!-- HPCToolkit Experiment DTD --> n<!-- Version 2.1 --> n<!-- ********************************************************************--> n<!ELEMENT HPCToolkitExperiment(Header,(SecCallPathProfile|SecFlatProfile) *)> n<!ATTLIST HPCToolkitExperiment\n version CDATA #REQUIRED > n n<!-- ******************************************************************--> n n<!-- Info/NV:flexible name-value pairs:(n) ame;(t) ype;(v) alue --> n<!ELEMENT Info(NV *)> n<!ATTLIST Info\n n CDATA #IMPLIED > n<!ELEMENT NV EMPTY > n<!ATTLIST NV\n n CDATA #REQUIRED\n t CDATA #IMPLIED\n v CDATA #REQUIRED > n n<!-- ******************************************************************--> n<!-- Header --> n<!-- ******************************************************************--> n<!ELEMENT Header(Info *)> n<!ATTLIST Header\n n CDATA #REQUIRED > n n<!-- ******************************************************************--> n<!-- Section Header --> n<!-- ******************************************************************--> n<!ELEMENT SecHeader(MetricTable?, MetricDBTable?, TraceDBTable?, LoadModuleTable?, FileTable?, ProcedureTable?, Info *)> n n<!-- MetricTable:--> n<!ELEMENT MetricTable(Metric) * > n n<!-- Metric:(i) d;(n) ame --> n<!--(v) alue-type:transient type of values --> n<!--(t) ype:persistent type of metric --> n<!-- fmt:format;show;--> n<!ELEMENT Metric(MetricFormula *, Info?)> n<!ATTLIST Metric\n i CDATA #REQUIRED\n n CDATA #REQUIRED\n es CDATA #IMPLIED\n em CDATA #IMPLIED\n ep CDATA #IMPLIED\n v(raw|final|derived-incr|derived) \"raw\\ t (inclusive|exclusive|nil) \nil\\ partner CDATA #IMPLIED\ fmt CDATA #IMPLIED\ show (1|0) \1\\ show-percent (1|0) \1> n n<!-- MetricFormula represents derived metrics: (t)ype; (frm): formula --> n<!ELEMENT MetricFormula (Info?)> n<!ATTLIST MetricFormula\ t (combine|finalize) \finalize\\ i CDATA #IMPLIED\ frm CDATA #REQUIRED> n n<!-- Metric data, used in sections: (n)ame [from Metric]; (v)alue --> n<!ELEMENT M EMPTY> n<!ATTLIST M\ n CDATA #REQUIRED\ v CDATA #REQUIRED> n n<!-- MetricDBTable: --> n<!ELEMENT MetricDBTable (MetricDB)*> n n<!-- MetricDB: (i)d; (n)ame --> n<!-- (t)ype: persistent type of metric --> n<!-- db-glob: file glob describing files in metric db --> n<!-- db-id: id within metric db --> n<!-- db-num-metrics: number of metrics in db --> n<!-- db-header-sz: size (in bytes) of a db file header --> n<!ELEMENT MetricDB EMPTY> n<!ATTLIST MetricDB\ i CDATA #REQUIRED\ n CDATA #REQUIRED\ t (inclusive|exclusive|nil) \nil\\ partner CDATA #IMPLIED\ db-glob CDATA #IMPLIED\ db-id CDATA #IMPLIED\ db-num-metrics CDATA #IMPLIED\ db-header-sz CDATA #IMPLIED> n n<!-- TraceDBTable: --> n<!ELEMENT TraceDBTable (TraceDB)> n n<!-- TraceDB: (i)d --> n<!-- db-min-time: min beginning time stamp (global) --> n<!-- db-max-time: max ending time stamp (global) --> n<!ELEMENT TraceDB EMPTY> n<!ATTLIST TraceDB\ i CDATA #REQUIRED\ db-glob CDATA #IMPLIED\ db-min-time CDATA #IMPLIED\ db-max-time CDATA #IMPLIED\ db-header-sz CDATA #IMPLIED> n n<!-- LoadModuleTable assigns a short name to a load module --> n<!ELEMENT LoadModuleTable (LoadModule)*> n n<!ELEMENT LoadModule (Info?)> n<!ATTLIST LoadModule\ i CDATA #REQUIRED\ n CDATA #REQUIRED> n n<!-- FileTable assigns a short name to a file --> n<!ELEMENT FileTable (File)*> n n<!ELEMENT File (Info?)> n<!ATTLIST File\ i CDATA #REQUIRED\ n CDATA #REQUIRED> n n<!-- ProcedureTable assigns a short name to a procedure --> n<!ELEMENT ProcedureTable (Procedure)*> n n<!-- Info/NV: flexible name-value pairs: (n)ame; (t)ype; (v)alue --> n<!-- f: family of the procedure (fake, root, ...)--> n<!ELEMENT Procedure (Info?)> n<!ATTLIST Procedure\ i CDATA #REQUIRED\ n CDATA #REQUIRED\ f CDATA #IMPLIED> n n<!-- ****************************************************************** --> n<!-- Section: Call path profile --> n<!-- ****************************************************************** --> n<!ELEMENT SecCallPathProfile (SecHeader, SecCallPathProfileData)> n<!ATTLIST SecCallPathProfile\ i CDATA #REQUIRED\ n CDATA #REQUIRED> n n<!ELEMENT SecCallPathProfileData (PF|M)*> n<!-- Procedure frame --> n<!-- (i)d: unique identifier for cross referencing --> n<!-- (s)tatic scope id --> n<!-- (n)ame: a string or an id in ProcedureTable --> n<!-- (lm) load module: a string or an id in LoadModuleTable --> n<!-- (f)ile name: a string or an id in LoadModuleTable --> n<!-- (l)ine range: \beg-end\ (inclusive range) --> n<!-- (a)lien: whether frame is alien to enclosing P --> n<!-- (str)uct: hpcstruct node id --> n<!-- (t)ype: hpcrun node type: memory access, variable declaration, ... --> n<!-- (v)ma-range-set: \{[beg-end), [beg-end)...}\ --> n<!ELEMENT PF (PF|Pr|L|C|S|M)*> n<!ATTLIST PF\ i CDATA #IMPLIED\ s CDATA #IMPLIED\ n CDATA #REQUIRED\ lm CDATA #IMPLIED\ f CDATA #IMPLIED\ l CDATA #IMPLIED\ str CDATA #IMPLIED\ v CDATA #IMPLIED> n<!-- Procedure (static): GOAL: replace with 'P' --> n<!ELEMENT Pr (Pr|L|C|S|M)*> n<!ATTLIST Pr\ i CDATA #IMPLIED\ s CDATA #IMPLIED\ n CDATA #REQUIRED\ lm CDATA #IMPLIED\ f CDATA #IMPLIED\ l CDATA #IMPLIED\ a (1|0) \0\\ str CDATA #IMPLIED\ v CDATA #IMPLIED> n<!-- Callsite (a special StatementRange) --> n<!ELEMENT C (PF|M)*> n<!ATTLIST C\ i CDATA #IMPLIED\ s CDATA #IMPLIED\ l CDATA #IMPLIED\ str CDATA #IMPLIED\ v CDATA #IMPLIED> n n<!-- ****************************************************************** --> n<!-- Section: Flat profile --> n<!-- ****************************************************************** --> n<!ELEMENT SecFlatProfile (SecHeader, SecFlatProfileData)> n<!ATTLIST SecFlatProfile\ i CDATA #REQUIRED\ n CDATA #REQUIRED> n n<!ELEMENT SecFlatProfileData (LM|M)*> n<!-- Load module: (i)d; (n)ame; (v)ma-range-set --> n<!ELEMENT LM (F|P|M)*> n<!ATTLIST LM\ i CDATA #IMPLIED\ n CDATA #REQUIRED\ v CDATA #IMPLIED> n<!-- File --> n<!ELEMENT F (P|L|S|M)*> n<!ATTLIST F\ i CDATA #IMPLIED\ n CDATA #REQUIRED> n<!-- Procedure (Note 1) --> n<!ELEMENT P (P|A|L|S|C|M)*> n<!ATTLIST P\ i CDATA #IMPLIED\ n CDATA #REQUIRED\ l CDATA #IMPLIED\ str CDATA #IMPLIED\ v CDATA #IMPLIED> n<!-- Alien (Note 1) --> n<!ELEMENT A (A|L|S|C|M)*> n<!ATTLIST A\ i CDATA #IMPLIED\ f CDATA #IMPLIED\ n CDATA #IMPLIED\ l CDATA #IMPLIED\ str CDATA #IMPLIED\ v CDATA #IMPLIED> n<!-- Loop (Note 1,2) --> n<!ELEMENT L (A|Pr|L|S|C|M)*> n<!ATTLIST L\ i CDATA #IMPLIED\ s CDATA #IMPLIED\ l CDATA #IMPLIED\ f CDATA #IMPLIED\ str CDATA #IMPLIED\ v CDATA #IMPLIED> n<!-- Statement (Note 2) --> n<!-- (it): trace record identifier --> n<!ELEMENT S (S|M)*> n<!ATTLIST S\ i CDATA #IMPLIED\ it CDATA #IMPLIED\ s CDATA #IMPLIED\ l CDATA #IMPLIED\ str CDATA #IMPLIED\ v CDATA #IMPLIED> n<!-- Note 1: Contained Cs may not contain PFs --> n<!-- Note 2: The 's' attribute is not used for flat profiles --> n
virtual NonUniformDegreeTreeNode * Current() const
CCT::Tree * cct() const