Motr  M0
md_req.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 sys
21 from addb2db import *
22 from typing import List, Dict
23 from graphviz import Digraph
24 from copy import deepcopy
25 from req_utils import *
26 
27 
28 def _graph_add_relations(graph, relations, is_meta=False):
29  # relation | from | to | table or direct| flags
30  # | table/mapping | table/mapping | mapping |
31  schema = [("client_id" , "client_id" , "dix_id" , "" , "C1"),
32  ("cas_id" , "cas_id" , "rpc_id" , "cas_to_rpc" , "C" ),
33  ("crpc_id" , "crpc_id" , "srpc_id" , "" , "C1"),
34  ("crpc_id" , "rpc_id" , "bulk_id" , "bulk_to_rpc" , "Cs"),
35  ("srpc_id" , "srpc_id" , "fom_id" , "" , "S1"),
36  ("fom_id" , "fom_id" , "tx_id" , "" , "S1"),
37  ("tx_id" , "" , "" , "" , "Sl"),
38  ("fom_id" , "fom_id" , "crow_fom_id" , "" , "S1"),
39  ("crow_fom_id", "crow_fom_id", "crow_tx_id" , "" , "S1"),
40  ("crow_tx_id" , "" , "" , "" , "Sl")]
41  # flags: '1' - one to one mapping, 's' - stash samples, 'l' - leaf element
42 
43  if is_meta:
44  schema.insert(1, ("dix_id", "dix_id", "mdix_id", "dix_to_mdix", "C"))
45  schema.insert(2, ("mdix_id", "dix_id", "cas_id", "dix_to_cas", "C"))
46  else:
47  schema.insert(1, ("dix_id", "dix_id", "cas_id", "dix_to_cas", "C"))
48 
49  graph_add_relations(graph, relations, schema)
50 
51 def graph_build(m_relations, relations, ext_graph: Digraph=None):
52  graph = ext_graph if ext_graph is not None else Digraph(
53  strict=True, format='png', node_attr = {'shape': 'plaintext'})
54 
55  if m_relations:
56  _graph_add_relations(graph, m_relations, True)
57  if relations:
58  _graph_add_relations(graph, relations)
59 
60  if ext_graph is None:
61  rel = relations if relations else m_relations
62  graph.render(filename='md_graph_{}'.format(rel[0]['client_id']))
63 
64  return graph
65 
66 # ================================================================================
67 # obtain some samples
68 
69 query_gen="""
70 SELECT
71 
72 DISTINCT(cas_to_rpc.rpc_id),
73 client_to_dix.client_id,
74 client_to_dix.dix_id,
75 {}
76 dix_to_cas.cas_id,
77 rpc_to_sxid.opcode,
78 rpc_to_sxid.xid, rpc_to_sxid.session_id,
79 sxid_to_rpc.xid, sxid_to_rpc.session_id,
80 fom_desc.rpc_sm_id, fom_desc.fom_sm_id, fom_desc.fom_state_sm_id,
81 fom_to_tx.tx_id, tx_to_gr.gr_id,
82 crow_fom_desc.fom_sm_id, crow_fom_desc.fom_state_sm_id,
83 crow_fom_to_tx.tx_id,
84 client_to_dix.pid,
85 fom_desc.pid
86 
87 FROM client_to_dix
88 {}
89 JOIN dix_to_cas on {}=dix_to_cas.dix_id
90 JOIN cas_to_rpc on dix_to_cas.cas_id=cas_to_rpc.cas_id
91 JOIN rpc_to_sxid on cas_to_rpc.rpc_id=rpc_to_sxid.id
92 JOIN sxid_to_rpc on rpc_to_sxid.xid=sxid_to_rpc.xid AND rpc_to_sxid.session_id=sxid_to_rpc.session_id
93 JOIN fom_desc on sxid_to_rpc.id=fom_desc.rpc_sm_id
94 LEFT JOIN fom_to_tx on fom_desc.fom_sm_id=fom_to_tx.fom_id
95 LEFT JOIN tx_to_gr on fom_to_tx.tx_id=tx_to_gr.tx_id
96 LEFT JOIN cas_fom_to_crow_fom on fom_desc.fom_sm_id=cas_fom_to_crow_fom.fom_id
97 LEFT JOIN fom_desc crow_fom_desc on cas_fom_to_crow_fom.crow_fom_id=crow_fom_desc.fom_sm_id
98 LEFT JOIN fom_to_tx crow_fom_to_tx on crow_fom_desc.fom_sm_id=crow_fom_to_tx.fom_id
99 
100 WHERE sxid_to_rpc.opcode in (230,231,232,233) and rpc_to_sxid.opcode in (230,231,232,233)
101 AND rpc_to_sxid.xid > 0
102 AND rpc_to_sxid.session_id > 0
103 {}
104 AND client_to_dix.pid=dix_to_cas.pid
105 AND client_to_dix.pid=cas_to_rpc.pid AND client_to_dix.pid=rpc_to_sxid.pid
106 
107 AND sxid_to_rpc.pid=fom_desc.pid
108 AND (fom_to_tx.tx_id is NULL OR fom_desc.pid=fom_to_tx.pid)
109 AND (tx_to_gr.gr_id is NULL OR fom_desc.pid=tx_to_gr.pid)
110 
111 AND (crow_fom_desc.fom_sm_id is NULL OR fom_desc.pid=crow_fom_desc.pid)
112 AND (crow_fom_to_tx.tx_id is NULL OR fom_desc.pid=crow_fom_to_tx.pid)
113 AND (tx_to_gr.gr_id is NULL OR fom_desc.pid=tx_to_gr.pid)
114 
115 AND client_to_dix.client_id={};
116 """
117 
118 def update_tables_common(relations, grange: int, time_table, client_start_time):
119  rpc_fom_s = set([(o['cas_id'],
120  o['opcode'],
121  o['cli_pid'],
122  o['srv_pid'],
123  o['crpc_id'],
124  o['srpc_id'],
125  o['fom_id'],
126  o['fom_state_id'],
127  o['tx_id'],
128  o['gr_id'],
129  o['crow_fom_id'],
130  o['crow_fom_state_id'],
131  o['crow_tx_id']) for o in relations])
132 
133  for cas_id, opcode, cli_pid, srv_pid, crpc_id, srpc_id, fom_id, fom_state_id, \
134  tx_id, gr_id, crow_fom_id, crow_fom_state_id, crow_tx_id in rpc_fom_s:
135  cas_req_d = query2dlist(cas_req.select().where((cas_req.id==cas_id)&
136  (cas_req.pid==cli_pid)))
137  times_tag_append(cas_req_d, 'op', f"cas {cas_id}")
138  time_table.append(cas_req_d)
139 
140  for tag,(rpc_id,pid) in {"c": (crpc_id, cli_pid),
141  "s": (srpc_id, srv_pid)}.items():
142  rpc_req_d = query2dlist(rpc_req.select().where((rpc_req.id==rpc_id)&
143  (rpc_req.pid==pid)))
144  times_tag_append(rpc_req_d, 'op', f"{tag}rpc[{opcode}] {rpc_id}")
145  time_table.append(rpc_req_d)
146 
147  fom_req_st_d = query2dlist(fom_req_state.select().where((fom_req_state.id==fom_state_id)&
148  (fom_req_state.pid==srv_pid)))
149  times_tag_append(fom_req_st_d, 'op', f"fom-state {fom_state_id}")
150  time_table.append(fom_req_st_d)
151 
152  fom_req_d = query2dlist(fom_req.select().where((fom_req.id==fom_id)&
153  (fom_req.pid==srv_pid)))
154  times_tag_append(fom_req_d, 'op', f"fom-phase {fom_id}")
155  time_table.append(fom_req_d)
156 
157  if tx_id:
158  be_tx_d = query2dlist(be_tx.select().where((be_tx.id==tx_id)&
159  (be_tx.pid==srv_pid)))
160  times_tag_append(be_tx_d, 'op', f"be_tx {tx_id}")
161  time_table.append(be_tx_d)
162 
163  left=[]
164  right=[]
165  if grange[0]>0:
166  left = query2dlist(fom_req.select().where((fom_req.id==gr_id)&
167  (fom_req.pid==srv_pid)&
168  (fom_req.time<client_start_time)).limit(grange[0]))
169  if grange[1]>0:
170  right = query2dlist(fom_req.select().where((fom_req.id==gr_id)&
171  (fom_req.pid==srv_pid)&
172  (fom_req.time>=client_start_time)).limit(grange[1]))
173  if grange[0]>0 or grange[1]>0:
174  gr_d = [*left, *right]
175  times_tag_append(gr_d, 'op', f"tx-gr-phase {gr_id}")
176  time_table.append(gr_d)
177 
178  if crow_fom_id:
179  crow_fom_req_st_d = query2dlist(fom_req_state.select().\
180  where((fom_req_state.id==crow_fom_state_id)&
181  (fom_req_state.pid==srv_pid)))
182  times_tag_append(crow_fom_req_st_d, 'op', f"crow-fom-state {crow_fom_state_id}")
183  time_table.append(crow_fom_req_st_d)
184 
185  crow_fom_req_d = query2dlist(fom_req.select().where((fom_req.id==crow_fom_id)&
186  (fom_req.pid==srv_pid)))
187  times_tag_append(crow_fom_req_d, 'op', f"crow-fom-phase {crow_fom_id}")
188  time_table.append(crow_fom_req_d)
189 
190  if crow_tx_id:
191  crow_be_tx_d = query2dlist(be_tx.select().where((be_tx.id==crow_tx_id)&
192  (be_tx.pid==srv_pid)))
193  times_tag_append(crow_be_tx_d, 'op', f"crow_be_tx {crow_tx_id}")
194  time_table.append(crow_be_tx_d)
195 
196 
197 def get_timelines(client_id: str, grange: int, client_pid: int=None, create_attr_graph: bool=False,
198  export_only: bool=False, ext_graph: Digraph=None):
199  time_table = []
200  queue_table = []
201  queue_start_time = []
202  client_start_time = 0
203  attr_graph = None
204 
205  pid_filter = f"AND client_to_dix.pid={client_pid}" if client_pid is not None else ""
206  query = query_gen.format("", "", "client_to_dix.dix_id", pid_filter, client_id)
207  mquery = query_gen.format("dix_to_mdix.mdix_id,",
208  "JOIN dix_to_mdix on client_to_dix.dix_id=dix_to_mdix.dix_id",
209  "dix_to_mdix.mdix_id", pid_filter, client_id)
210 
211  with DB.atomic():
212  cursor = DB.execute_sql(query)
213  fields = list(cursor.fetchall())
214  cursor = DB.execute_sql(mquery)
215  m_fields = list(cursor.fetchall())
216  labels = ("crpc_id", "client_id", "dix_id", "cas_id", "opcode",
217  "cxid", "csess", "sxid", "ssess", "srpc_id", "fom_id",
218  "fom_state_id", "tx_id", "gr_id", "crow_fom_id", "crow_fom_state_id",
219  "crow_tx_id", "cli_pid", "srv_pid")
220  m_labels = labels[:3] + ("mdix_id",) + labels[3:]
221  relations = [dict(zip(labels, f)) for f in fields]
222  m_relations = [dict(zip(m_labels, f)) for f in m_fields]
223 
224  #print(m_relations)
225  #print(relations)
226 
227  if not relations and not m_relations:
228  print("Incomplete data for client id {} (pid {})".format(client_id, client_pid))
229  exit(1)
230 
231  client_id = set([o['client_id'] for o in relations]).pop()
232  if relations:
233  dix_id = set([o['dix_id'] for o in relations]).pop()
234  if m_relations:
235  m_dix_id = set([o['mdix_id'] for o in m_relations]).pop()
236 
237  client_req_d = query2dlist(
238  client_req.select().where(client_req.id==client_id) if client_pid is None else
239  client_req.select().where((client_req.id==client_id)&(client_req.pid==client_pid)))
240  times_tag_append(client_req_d, 'op', f"client {client_id}")
241  time_table.append(client_req_d)
242  client_start_time=min([t['time'] for t in client_req_d])
243 
244  if relations:
245  dix_req_d = query2dlist(
246  dix_req.select().where(dix_req.id==dix_id) if client_pid is None else
247  dix_req.select().where((dix_req.id==dix_id)&(dix_req.pid==client_pid)))
248  times_tag_append(dix_req_d, 'op', f"dix {dix_id}")
249  time_table.append(dix_req_d)
250 
251  if m_relations:
252  m_dix_req_d = query2dlist(
253  dix_req.select().where(dix_req.id==m_dix_id) if client_pid is None else
254  dix_req.select().where((dix_req.id==m_dix_id)&(dix_req.pid==client_pid)))
255  times_tag_append(m_dix_req_d, 'op', f"mdix {m_dix_id}")
256  time_table.append(m_dix_req_d)
257  update_tables_common(m_relations, grange, time_table, client_start_time)
258 
259  if relations:
260  update_tables_common(relations, grange, time_table, client_start_time)
261 
262  # process client requests and subrequests
263  #time_table=[t for t in time_table if t != []] ### XXX: filter non-exsitent txs!
264  if not export_only:
265  prepare_time_table(time_table)
266 
267  # attr
268  if create_attr_graph:
269  print("Building attributes graph...")
270  attr_graph = graph_build(m_relations, relations, ext_graph)
271 
272  return time_table, queue_table, queue_start_time, client_start_time, attr_graph
273 
275  parser = argparse.ArgumentParser(prog=sys.argv[0], description="""
276  md_req.py: Display significant performance counters for MD Motr stack.
277  """)
278  parser.add_argument("-p", "--pid", type=int, default=None,
279  help="Client pid to get requests for")
280  parser.add_argument("-m", "--maximize", action='store_true', help="Display in maximised window")
281  parser.add_argument("-q", "--queues", action='store_true', help="Display queues also")
282  parser.add_argument("-r", "--qrange", type=int, default=10,
283  help="Limit quantity of queue-related samples")
284  parser.add_argument("-g", "--grange", default=[0, 0],
285  nargs=2, metavar=('left', 'right'), type=int,
286  help="Limit quantity of tx group-related samples")
287  parser.add_argument("-a", "--attr", action='store_true', help="Create attributes graph")
288  parser.add_argument("-v", "--verbose", action='count', default=0)
289  parser.add_argument("-u", "--time-unit", choices=['ms','us'], default='us',
290  help="Default time unit")
291  parser.add_argument("-d", "--db", type=str, default="m0play.db",
292  help="Performance database (m0play.db)")
293  parser.add_argument("client_id", type=str, help="Client request id")
294 
295  return parser.parse_args()
296 
297 if __name__ == '__main__':
298  args=parse_args()
299 
300  db_init(args.db)
301  db_connect()
302 
303  print("Getting timelines...")
304 
305  time_table, queue_table, queue_start_time, client_start_time, _ = \
306  get_timelines(args.client_id, args.grange, args.pid, args.attr, False)
307 
308  if args.queues:
309  fill_queue_table(queue_table, queue_start_time)
310 
311  db_close()
312 
313  print("Plotting timelines...")
314 
315  draw_timelines(time_table, queue_table, client_start_time, queue_start_time,
316  args.time_unit, args.queues, args.maximize)
317 
318 # ================================================================================
static struct m0_list list
Definition: list.c:144
def prepare_time_table(time_table)
Definition: req_utils.py:51
def query2dlist(model)
Definition: req_utils.py:68
def db_init(path)
Definition: addb2db.py:314
def db_connect()
Definition: addb2db.py:321
static M0_UNUSED void print(struct m0_be_list *list)
Definition: list.c:186
def times_tag_append
Definition: req_utils.py:74
def graph_build
Definition: md_req.py:51
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 parse_args()
Definition: md_req.py:274
def db_close()
Definition: addb2db.py:324
def graph_add_relations
Definition: req_utils.py:227
def get_timelines
Definition: md_req.py:197
def draw_timelines
Definition: req_utils.py:105
def update_tables_common
Definition: md_req.py:118