Motr  M0
req_utils.py
Go to the documentation of this file.
1 #
2 # Copyright (c) 2020 Seagate Technology LLC and/or its Affiliates
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License");
5 # you may not use this file except in compliance with the License.
6 # You may obtain a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15 #
16 # For any questions about this software or licensing,
17 # please email opensource@seagate.com or cortx-questions@seagate.com.
18 #
19 
20 import re
21 import math
22 import numpy as np
23 import matplotlib.pyplot as plt
24 from addb2db import *
25 from playhouse.shortcuts import model_to_dict
26 from typing import List, Dict
27 from graphviz import Digraph
28 
29 # time convertor
30 CONV={"us": 1000, "ms": 1000*1000}
31 
32 def draw_timeline(timeline, offset):
33  for i in range(len(timeline)-1):
34  color = ['red', 'green', 'blue', 'yellow', 'cyan'][i%5]
35  start = timeline[i]['time']
36  end = timeline[i+1]['time']
37  label = timeline[i]['state']
38 
39  plt.hlines(offset, start, end, colors=color, lw=5)
40  plt.text(start, offset, label, rotation=90)
41 
42  start = timeline[0]['time']
43  end = timeline[-1]['time']
44  label = timeline[-1]['state']
45  plt.text(end, offset, label, rotation=90)
46 
47  center = (end-start)/2
48  label = timeline[0]['op'] + ": " + str(round((end-start)/1000000,3)) + "ms"
49  plt.text(start+center, offset+0.01, label)
50 
51 def prepare_time_table(time_table):
52  ref_time = -1
53 
54  for times in time_table:
55  times.sort(key=lambda time: time['time'])
56 
57  if ref_time == -1:
58  ref_time = times[0]['time']
59  else:
60  ref_time = min(times[0]['time'], ref_time)
61 
62  for times in time_table:
63  for time in times:
64  time['time']=time['time']-ref_time
65 
66  return ref_time
67 
68 def query2dlist(model):
69  out=[]
70  for m in model:
71  out.append(model_to_dict(m))
72  return out
73 
74 def times_tag_append(times: List[Dict], tag_name: str, tag_value: str):
75  for t in times:
76  t[tag_name] = tag_value
77 queues_tag_append=times_tag_append
78 
79 def draw_queue_line(queue, offset):
80  mx=max([q['max'] for q in queue])
81  mx=0.0000001 if mx == 0 else mx #XXX
82 
83  qnr =[q['nr'] for q in queue]
84  xdata=[q['time'] for q in queue]
85  ydata=[q['avg'] for q in queue]
86  _min =[q['min'] for q in queue]
87  _max =[q['max'] for q in queue]
88  dev =[math.sqrt(q['dev']) for q in queue]
89  avi =[q['avg']-math.sqrt(q['dev']) for q in queue]
90  ava =[q['avg']+math.sqrt(q['dev']) for q in queue]
91 
92  plt.plot(xdata, [offset+y/mx for y in ydata], 'or')
93  plt.plot(xdata, [offset+y/mx for y in ydata], '-', color='gray')
94  plt.fill_between(xdata,
95  [offset+y/mx for y in avi],
96  [offset+y/mx for y in ava], color='gray', alpha=0.2)
97  plt.fill_between(xdata,
98  [offset+y/mx for y in _min],
99  [offset+y/mx for y in _max], color='green', alpha=0.2)
100 
101  for x,y,nr,i,a,d in zip(xdata, ydata, qnr, _min, _max, dev ):
102  plt.text(x,offset+y/mx, f"{round(y,2)} |{round(a,2)}|")
103 
104 def draw_timelines(time_table, queue_table, client_start_time, queue_start_time,
105  time_unit: str, show_queues: bool, maximize: bool):
106  cursor={"x0":0, "y0":0, "x1": 0, "y1": 0, "on": False}
107  undo=[]
108  def onpress(event):
109  if event.key == 'a':
110  cursor.update({ "on": True })
111  elif event.key == 'd':
112  if undo:
113  for an in undo.pop():
114  an.remove()
115  event.canvas.draw()
116 
117  def onrelease(event):
118  if not cursor["on"]:
119  return
120  cursor.update({ "x1": event.xdata, "y1": event.ydata })
121  cursor.update({ "on": False })
122 
123  an1=event.inaxes.axvspan(cursor["x0"], cursor["x1"], facecolor='0.9', alpha=.5)
124  an2=event.inaxes.annotate('', xy=(cursor["x0"], cursor["y0"]),
125  xytext=(cursor["x1"], cursor["y0"]),
126  xycoords='data', textcoords='data',
127  arrowprops={'arrowstyle': '|-|'})
128  an3=event.inaxes.annotate(str(round((cursor["x1"]-cursor["x0"])/CONV[time_unit],2))+f" {time_unit}",
129  xy=(min(cursor["x1"], cursor["x0"])+
130  abs(cursor["x1"]-cursor["x0"])/2, 0.5+cursor["y0"]),
131  ha='center', va='center')
132  undo.append([an1, an2, an3])
133  event.canvas.draw()
134 
135  def onclick(event):
136  if not cursor["on"]:
137  return
138  cursor.update({ "x0": event.xdata, "y0": event.ydata })
139 
140  fig = plt.figure()
141  fig.canvas.mpl_connect('key_press_event', onpress)
142  fig.canvas.mpl_connect('button_press_event', onclick)
143  fig.canvas.mpl_connect('button_release_event', onrelease)
144 # ============================================================================
145 
146  plt.subplot({True:211, False:111}[show_queues])
147  offset=0.0
148  for times in time_table:
149  draw_timeline(times, offset)
150  offset=offset-2
151 
152  plt.yticks(np.arange(0.0, offset, -2), [t[0]['op'] for t in time_table])
153  plt.grid(True)
154 
155  #queues
156  if show_queues:
157  for qt,subp,qstt in zip(queue_table, [223,224], queue_start_time):
158  plt.subplot(subp)
159  offset=0.0
160  for queue in qt:
161  draw_queue_line(queue, offset)
162  offset=offset-2
163 
164  plt.axvline(x=(client_start_time-qstt))
165  plt.yticks(np.arange(0.0, offset, -2), [t[0]['op'] for t in qt])
166  plt.grid(True)
167 
168  #show
169  if maximize:
170  mng = plt.get_current_fig_manager()
171  mng.window.maximize()
172  plt.show()
173 
174 def fill_queue_table(queue_table, queue_start_time, qrange: int):
175  for q_conf in [["runq", "wail", "fom-active"],
176  ["stob-ioq-inflight", "stob-ioq-queued"]]:
177  queue_table.append([])
178  loc_nr = queues.select(fn.MAX(queues.locality)).where(queues.type==q_conf[0]).scalar()
179  for nr in range(loc_nr+1):
180  for queuei in q_conf:
181  left = queues.select().where((queues.min>-1)
182  &(queues.locality==nr)
183  &(queues.type==queuei)
184  &(queues.time<client_start_time)).limit(qrange)
185  right = queues.select().where((queues.min>-1)
186  &(queues.locality==nr)
187  &(queues.type==queuei)
188  &(queues.time>=client_start_time)).limit(qrange)
189  stmt = [*left, *right]
190  #print(stmt.sql())
191  q_d = query2dlist(stmt)
192  _min = queues.select(fn.MIN(queues.min)).where((queues.min>-1)
193  &(queues.locality==nr)
194  &(queues.type==queuei)).scalar()
195  _max = queues.select(fn.MAX(queues.max)).where((queues.min>-1)
196  &(queues.locality==nr)
197  &(queues.type==queuei)).scalar()
198  queues_tag_append(q_d, 'op', f"{queuei}#{nr}[{_min}..{_max}]")
199  queue_table[-1].append(q_d)
200 
201  queue_start_time.append(prepare_time_table(queue_table[-1]))
202 
203 
204 # =============== GRAPH-RELATED STUFF ===============
205 
206 def graph_node_add(g: Digraph, name: str, header: str, attrs: Dict):
207  node_template="""<
208 <TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">
209  <TR>
210  <TD>{}</TD>
211  </TR>
212  <TR>
213  <TD>{}</TD>
214  </TR>
215 </TABLE>>
216 """
217  label = "<BR/>".join([f"{k}={v}" for (k,v) in attrs.items()])
218  g.node(name, node_template.format(header, label))
219 
220 def graph_node_add_attr(g: Digraph, lid: int, rel: str, pid: int):
221  c = lambda attr: re.sub("M0_AVI_.*_ATTR_", "", attr)
222  attrd = query2dlist(attr.select().where( (attr.entity_id==lid)
223  &(attr.pid==pid)))
224  attrs = dict([(c(a['name']), a['val']) for a in attrd])
225  graph_node_add(g, f"{lid}", "{}={} pid={}".format(rel, lid, pid), attrs)
226 
227 def graph_add_relations(graph: Digraph, relations, schema):
228  def pid_get(lid_rec, flags: str):
229  pidt=next(filter(lambda f: f in "SC", flags))
230  return { 'C' : lid_rec[1],
231  'S' : lid_rec[2] }[pidt]
232  '''
233  Schema example:
234  # relation | from | to | table or direct| flags
235  # | table/mapping | table/mapping | mapping |
236  schema = [("client_id" , "client_id" , "dix_id" , "" , "C1"),
237  ("cas_id" , "cas_id" , "rpc_id" , "cas_to_rpc" , "C" ),
238  ("crpc_id" , "crpc_id" , "srpc_id" , "" , "C1"),
239  ("crpc_id" , "rpc_id" , "bulk_id" , "bulk_to_rpc" , "Cs"),
240  ("srpc_id" , "srpc_id" , "fom_id" , "" , "S1"),
241  ("fom_id" , "fom_id" , "tx_id" , "" , "S1"),
242  ("tx_id" , "" , "" , "" , "Sl"),
243  ("fom_id" , "fom_id" , "crow_fom_id" , "" , "S1"),
244  ("crow_fom_id", "crow_fom_id", "crow_tx_id" , "" , "S1"),
245  ("crow_tx_id" , "" , "" , "" , "Sl")]
246  # flags: '1' - one to one mapping, 's' - stash samples, 'l' - leaf element
247  '''
248 
249  stash=[]
250  for rel,fr,to,map,flags in schema:
251  layer_ids=set([(r[rel], r['cli_pid'], r['srv_pid']) for r in relations if r[rel] is not None])
252  for lid_rec in layer_ids:
253  lid = lid_rec[0]
254  pid = pid_get(lid_rec, flags)
255  graph_node_add_attr(graph, lid, rel, pid)
256 
257  if "l" in flags:
258  continue
259  if "1" in flags:
260  tid = next(r[to] for r in relations if r[fr]==lid)
261  if tid:
262  graph.edge(f"{lid}", f"{tid}")
263  continue
264 
265  cursor = DB.execute_sql(f"SELECT {to} FROM {map} WHERE {fr}={lid} and pid={pid}")
266  for (tid,) in cursor:
267  graph.edge(f"{lid}", f"{tid}")
268  if "s" in flags:
269  stash.append((tid,to,pid))
270 
271  for lid,rel,pid in stash:
272  graph_node_add_attr(graph, lid, rel, pid)
def graph_node_add
Definition: req_utils.py:206
def draw_queue_line(queue, offset)
Definition: req_utils.py:79
def prepare_time_table(time_table)
Definition: req_utils.py:51
def query2dlist(model)
Definition: req_utils.py:68
Definition: filter.py:1
def times_tag_append
Definition: req_utils.py:74
def graph_node_add_attr
Definition: req_utils.py:220
static struct m0_addb2_callback c
Definition: consumer.c:41
static long long max(long long a, long long b)
Definition: crate.c:196
static int next[]
Definition: cp.c:248
def fill_queue_table
Definition: req_utils.py:174
format
Definition: hist.py:128
static long long min(long long a, long long b)
Definition: crate.c:191
def graph_add_relations
Definition: req_utils.py:227
def draw_timelines
Definition: req_utils.py:105
def draw_timeline(timeline, offset)
Definition: req_utils.py:32