20 """trace class accepts a stream of incoming records (represented by 21 the record class) and produces the output in the form of an SVG image, 22 describing the stream. 24 Some assumptions are made about the input stream: 26 - all records are from the same process, 28 - the time-stamps of incoming records are monotonically 29 increasing. m0addb2dump output does not necessarily conform to this 30 restriction. This can always be fixed by doing 32 m0addb2dump -f | sort -k2.1,2.29 -s | m0addb2dump -d 34 - if a trace does not contain all records produced by a Motr process from 35 start-up to shutdown, certain corner cases (like re-use of the same 36 memory address for a new fom), can result in an incorrect 37 interpretation of a final portion of the trace. 41 The entirety of the output image is divided into vertical stripes, 42 corresponding to the localities, divided by 10px-wide vertical lines . The 43 number of localities is specified by the "loc_nr" parameter. 45 Vertical axis corresponds to time (from top to bottom). Horizontal dashed 46 lines mark time-steps with the granularity specified by the "step" 49 In the area, corresponding to a locality, the foms, executed in this 50 locality are represented. Each fom is represented as a rectangle with fixed 51 width and with the height corresponding to the fom life-time. The left 52 border of this rectangle is marked by a black line, other borders are 55 The interior of a fom rectangle is divided into 3 vertical "lanes". The 56 first lane is used for labels. When a fom is created, its type and address 57 are written to the label lane. When the fom experiences a phase transition, 58 the name of the new phase is written to the label lane. 60 The second lane, represents phases, marked by different colours. 62 The third lane contains states, marked by different colours. 64 The line at the left border of fom area can be missing if the final state 65 transition for the fom is missing from the log. 67 If fom phase state machine doesn't have transition descriptions, the phase 68 lane will be empty and phase labels will be missing. 70 By specifying "starttime" and "duration" parameters, view area can be 71 narrowed to produce more manageable images. When view area is narrowed, the 72 entire trace is still processed, but the SVG elements that would fall outside 73 of the visible image are omitted. 81 def __init__(self, width, height, loc_nr, duration, starttime = None,
82 step = 100, outname = "out.svg", maxfom = 20, verbosity = 0,
94 self.
usec = duration * 1000000
98 self.
out = svgwrite.Drawing(outname, profile=
'full', \
99 size = (str(width) +
"px",
122 self.
axis = svgwrite.rgb(0, 0, 0,
'%')
142 "stroke" : self.
axis,
144 "stroke_dasharray" :
"1,1" 151 for i
in range(loc_nr):
154 self.
line((x, 0), (x, height), stroke = self.
axis, stroke_width = 10)
155 self.
text(
"locality " + str(i), insert = (x + 10, 20))
156 for _
in range(self.
iomax):
157 self.
iolast.append(datetime.datetime(1970, 01, 01))
158 for _
in range(self.
netmax):
159 self.
netlast.append(datetime.datetime(1970, 01, 01))
161 stroke = self.
axis, stroke_width = 10)
164 stroke = self.
axis, stroke_width = 10)
167 stroke = self.
axis, stroke_width = 10)
183 assert 0 <= lane
and lane < self.
maxlane 189 interval = stamp - self.
start 190 usec = interval.microseconds + (interval.seconds +
191 interval.days * 24 * 3600) * 1000000
195 addr = rec.get(
"fom")
200 f.params = [
None,
None,
None,
None,
None, rec.ctx[
"fom"][1]]
206 seed = str +
"^" + str
207 red = hash(seed +
"r") % 90 208 green = hash(seed + "g") % 90
209 blue = hash(seed +
"b") % 90
210 return svgwrite.rgb(red, green, blue,
'%')
216 start = self.
getpos(start)
217 height = self.
getpos(end) - start
219 return {
"insert": (lane, start),
"size": (self.
lane_width, height) }
224 return svgwrite.rgb(100, 100, 0,
'%')
225 elif state ==
"Ready":
226 return svgwrite.rgb(100, 0, 0,
'%')
227 elif state ==
"Running":
228 return svgwrite.rgb(0, 100, 0,
'%')
229 elif state ==
"Waiting":
230 return svgwrite.rgb(0, 0, 100,
'%')
232 return svgwrite.rgb(10, 10, 10,
'%')
237 if y + h >= 0
and y < self.
height:
240 def line(self, start, end, **kw):
241 if end[1] >= 0
and start[1] < self.
height:
246 self.
line(start, end, **kw)
248 def text(self, text, connect = False, force = False, **kw):
249 x =
int(kw[
"insert"][0])
251 if not self.
label and not force:
253 if y >= 0
and y < self.
height:
259 print "Labels are overcrowded. Increase image height." 263 kw[
"insert"] = (x + 10, y)
264 kw[
"font_family"] =
"Courier" 267 self.
line((x, y0), (x + 10, y - 4), **self.
dash)
269 return x + 10 + len(text) * 9.5, y - 4
272 return self.
text(text, insert = (self.
getlane(fom, 0),
276 if self.
start ==
None:
279 duration = datetime.timedelta(microseconds = self.
usec)
281 delta = datetime.timedelta(milliseconds = self.
step)
283 while n*delta <= duration:
284 t = self.
start + n * delta
288 stroke_width = 1, stroke_dasharray =
"20,10,5,5,5,10")
289 self.
text(label, insert = (0, y - 10), force =
True)
293 def ioadd(self, time, fid, seconds):
294 duration = datetime.timedelta(microseconds =
float(seconds) * 1000000)
295 start = time - duration
298 l0 = self.
text(
"L " + fid, insert = (self.
iostart, y0))
299 l1 = self.
text(
"E " + fid, insert = (self.
iostart, y1))
300 slot =
next((i
for i
in range(len(self.
iolast))
if 301 self.
iolast[i] < start),
None)
304 self.
rect(insert = (x, y0), size = (self.
iolane * 3/4, y1 - y0),
305 fill = self.
getcolour(str(slot) + str(start)))
311 print "Too many concurrent IO-s. Increase iomax." 313 def netbufadd(self, time, buf, qtype, seconds, stime, status, length):
325 duration = datetime.timedelta(microseconds =
float(seconds) * 1000000)
326 dequeue = start + duration
327 assert start <= dequeue
and dequeue <= time
331 l0 = self.
text(
"Q " + buf +
" " + qname[qtype] +
" " + str(length),
333 l2 = self.
text(
"C " + buf, insert = (self.
netstart, y2))
334 slot =
next((i
for i
in range(len(self.
netlast))
if 335 self.
netlast[i] < start),
None)
338 self.
rect(insert = (x, y0), size = (self.
netlane * 3/4, y1 - y0),
340 self.
rect(insert = (x, y1), size = (self.
netlane * 1/4, y2 - y1),
347 print "Too many concurrent netbufs. Increase netmax." 349 def mutex(self, mname, label, time, seconds, addr):
350 duration = datetime.timedelta(microseconds =
float(seconds) * 1000000)
351 start = time - duration
354 exists = addr
in self.
locks 359 print "Too many locks. Increase lockmax." 362 lane = self.
locks[addr]
367 l = self.
text(mname +
" " + str(addr), insert = (self.
lockstart, ly))
369 self.
rect(insert = (x, y0), size = (self.
locklane * 3/4, y1 - y0),
381 for i
in range(len(self.
foms)):
382 if i
not in self.
foms:
386 if trace.warnedfom < j:
387 print (
"{}: too many concurrent foms, " 388 "increase maxfom to {}".
format(fom.time, j))
395 assert self.
foms[fom.loc_idx] == fom
396 del self.
foms[fom.loc_idx]
409 return datetime.datetime(year =
int(stamp[ 0: 4]),
410 month =
int(stamp[ 5: 7]),
411 day =
int(stamp[ 8:10]),
412 hour =
int(stamp[11:13]),
413 minute =
int(stamp[14:16]),
414 second =
int(stamp[17:19]),
415 microsecond =
int(stamp[20:26]))
424 obj.params = words[2:]
427 trace.prepare(obj.time)
436 assert key
not in self.ctx
442 trace.processed = trace.processed + 1
443 if (trace.verb > 0
and 444 self.time - trace.lastreport > datetime.timedelta(seconds = 1)):
445 print self.time, trace.processed - trace.reported, trace.processed
446 trace.lastreport = self.time
447 trace.reported = trace.processed
450 return self.ctx[label][0]
453 return "fom" in self.ctx
456 return str(self.time)
459 loc =
int(self.
get(
"locality"))
460 if self.
trace.loc_nr == 1:
462 assert 0 <= loc
and loc < self.
trace.loc_nr
467 state = self.params[2]
468 super(fstate, self).
done(trace)
470 fom = trace.fomfind(self)
471 trace.rect(fill = trace.statecolour(fom),
472 **trace.fomrect(fom, 3, fom.state_time, self.time))
473 fom.state_time = self.time
475 if state ==
"Finished":
476 start = trace.getpos(fom.time)
477 end = trace.getpos(self.time)
478 lane = trace.getlane(fom, 0) - 5
479 trace.line((lane, start), (lane, end), stroke = trace.axis,
481 self.
trace.fomdel(fom)
482 del trace.foms[self.
get(
"fom")]
487 super(fphase, self).
done(trace)
488 if (len(self.params)
in (2, 3)
and self.
fomexists()):
489 fom = trace.fomfind(self)
490 trace.rect(fill = trace.fomcolour(fom),
491 **trace.fomrect(fom, 2, fom.phase_time, self.time))
492 l = trace.fomtext(fom, fom.phase, fom.phase_time)
493 x = trace.getlane(fom, 1)
495 trace.tline(l, (x, l[1]), **trace.dash)
496 trace.tline((x, l[1]),
497 (trace.getlane(fom, 2), trace.getpos(fom.phase_time)),
499 fom.phase_time = self.time
500 fom.phase = self.params[-1]
504 addr = self.
get(
"fom")
505 assert "locality" in self.ctx
507 trace.foms[addr] = self
508 super(fom, self).
done(trace)
510 self.
trace.fomadd(self)
515 trace.fomtext(self, self.params[5] + str(addr) +
516 "[" + self.
get(
"locality") +
"]", self.time)
519 return str(self.time) +
" " + self.
get(
"fom")
523 if "locality" not in self.ctx:
525 super(forq, self).
done(trace)
527 nanoseconds =
float(self.params[0][:-1])
528 duration = datetime.timedelta(microseconds = nanoseconds / 1000)
530 y = self.
trace.getpos(self.time - duration)
531 trace.tline((x, y), (x, self.
trace.getpos(self.time)),
532 stroke = svgwrite.rgb(80, 10, 10,
'%'), stroke_width = 5)
533 trace.text(self.params[1], connect =
True, insert = (x + 10, y))
537 super(ioend, self).
done(trace)
538 trace.ioadd(self.time, self.params[0][:-1], self.params[2][:-1])
542 super(netbuf, self).
done(trace)
543 assert (self.params[0] ==
"buf:" and self.params[2] ==
"qtype:" and 544 self.params[4] ==
"time:" and self.params[6] ==
"duration:" 545 and self.params[8] ==
"status:" and self.params[10] ==
"len:")
546 trace.netbufadd(self.time,
547 buf = self.params[1][:-1],
548 qtype =
int(self.params[3][:-1]),
549 stime = self.params[5][:-1],
550 seconds =
float(self.params[7][:-1]),
551 status =
int(self.params[9][:-1]),
552 length =
int(self.params[11]))
560 super(mutex, self).
done(trace)
561 trace.mutex(self.
mname, self.
label, self.time,
562 float(self.params[0][:-1]), self.params[1])
566 self.
setname(
"rpc-mach",
"wait")
567 super(rpcmachwait, self).
done(trace)
571 self.
setname(
"rpc-mach",
"hold")
572 super(rpcmachhold, self).
done(trace)
576 "fom-state" : fstate,
577 "fom-phase" : fphase,
578 "loc-forq-duration" : forq,
579 "stob-io-end" : ioend,
581 "rpc-mach-wait" : rpcmachwait,
582 "rpc-mach-hold" : rpcmachhold
def __init__(self, trace, idx)
def mutex(self, mname, label, time, seconds, addr)
def netbufadd(self, time, buf, qtype, seconds, stime, status, length)
def line(self, start, end, kw)
def fomtext(self, fom, text, time)
def __init__(self, width, height, loc_nr, duration, starttime=None, step=100, outname="out.svg", maxfom=20, verbosity=0, label=True)
static int get(struct scanner *s, void *buf, size_t nob)
def tline(self, start, end, kw)
static long long max(long long a, long long b)
def getlane(self, fom, lane)
def setname(self, mname, label)
def fomrect(self, fom, lane, start, end)
def text(self, text, connect=False, force=False, kw)
def ioadd(self, time, fid, seconds)
def statecolour(self, fom)
static void add(struct m0_addb2_mach *mach, uint64_t id, int n, const uint64_t *value)