PageRenderTime 47ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/clion/plugins/python/helpers/pydev/pydevd_concurrency_analyser/pydevd_concurrency_logger.py

https://gitlab.com/ZoZworc/install
Python | 343 lines | 280 code | 41 blank | 22 comment | 47 complexity | 710a37c4bfd6264d6ece5101def7e8a6 MD5 | raw file
  1. import time
  2. from pydevd_concurrency_analyser.pydevd_thread_wrappers import ObjectWrapper, wrap_attr
  3. import pydevd_file_utils
  4. from _pydevd_bundle import pydevd_vars
  5. from _pydev_bundle._pydev_filesystem_encoding import getfilesystemencoding
  6. from _pydevd_bundle.pydevd_constants import dict_contains, get_thread_id, IS_PY3K
  7. file_system_encoding = getfilesystemencoding()
  8. try:
  9. from urllib import quote
  10. except:
  11. from urllib.parse import quote # @UnresolvedImport
  12. from _pydev_imps._pydev_saved_modules import threading
  13. threadingCurrentThread = threading.currentThread
  14. DONT_TRACE_THREADING = ['threading.py', 'pydevd.py']
  15. INNER_METHODS = ['_stop']
  16. INNER_FILES = ['threading.py']
  17. THREAD_METHODS = ['start', '_stop', 'join']
  18. LOCK_METHODS = ['__init__', 'acquire', 'release', '__enter__', '__exit__']
  19. QUEUE_METHODS = ['put', 'get']
  20. from _pydevd_bundle.pydevd_comm import GlobalDebuggerHolder, NetCommand
  21. import traceback
  22. import time
  23. # return time since epoch in milliseconds
  24. cur_time = lambda: int(round(time.time() * 1000000))
  25. try:
  26. import asyncio # @UnresolvedImport
  27. except:
  28. pass
  29. def get_text_list_for_frame(frame):
  30. # partial copy-paste from make_thread_suspend_str
  31. curFrame = frame
  32. cmdTextList = []
  33. try:
  34. while curFrame:
  35. #print cmdText
  36. myId = str(id(curFrame))
  37. #print "id is ", myId
  38. if curFrame.f_code is None:
  39. break #Iron Python sometimes does not have it!
  40. myName = curFrame.f_code.co_name #method name (if in method) or ? if global
  41. if myName is None:
  42. break #Iron Python sometimes does not have it!
  43. #print "name is ", myName
  44. filename = pydevd_file_utils.get_abs_path_real_path_and_base_from_frame(curFrame)[1]
  45. myFile = pydevd_file_utils.norm_file_to_client(filename)
  46. if file_system_encoding.lower() != "utf-8" and hasattr(myFile, "decode"):
  47. # myFile is a byte string encoded using the file system encoding
  48. # convert it to utf8
  49. myFile = myFile.decode(file_system_encoding).encode("utf-8")
  50. #print "file is ", myFile
  51. #myFile = inspect.getsourcefile(curFrame) or inspect.getfile(frame)
  52. myLine = str(curFrame.f_lineno)
  53. #print "line is ", myLine
  54. #the variables are all gotten 'on-demand'
  55. #variables = pydevd_vars.frame_vars_to_xml(curFrame.f_locals)
  56. variables = ''
  57. cmdTextList.append('<frame id="%s" name="%s" ' % (myId , pydevd_vars.make_valid_xml_value(myName)))
  58. cmdTextList.append('file="%s" line="%s">' % (quote(myFile, '/>_= \t'), myLine))
  59. cmdTextList.append(variables)
  60. cmdTextList.append("</frame>")
  61. curFrame = curFrame.f_back
  62. except :
  63. traceback.print_exc()
  64. return cmdTextList
  65. def send_message(event_class, time, name, thread_id, type, event, file, line, frame, lock_id=0, parent=None):
  66. dbg = GlobalDebuggerHolder.global_dbg
  67. cmdTextList = ['<xml>']
  68. cmdTextList.append('<' + event_class)
  69. cmdTextList.append(' time="%s"' % pydevd_vars.make_valid_xml_value(str(time)))
  70. cmdTextList.append(' name="%s"' % pydevd_vars.make_valid_xml_value(name))
  71. cmdTextList.append(' thread_id="%s"' % pydevd_vars.make_valid_xml_value(thread_id))
  72. cmdTextList.append(' type="%s"' % pydevd_vars.make_valid_xml_value(type))
  73. if type == "lock":
  74. cmdTextList.append(' lock_id="%s"' % pydevd_vars.make_valid_xml_value(str(lock_id)))
  75. if parent is not None:
  76. cmdTextList.append(' parent="%s"' % pydevd_vars.make_valid_xml_value(parent))
  77. cmdTextList.append(' event="%s"' % pydevd_vars.make_valid_xml_value(event))
  78. cmdTextList.append(' file="%s"' % pydevd_vars.make_valid_xml_value(file))
  79. cmdTextList.append(' line="%s"' % pydevd_vars.make_valid_xml_value(str(line)))
  80. cmdTextList.append('></' + event_class + '>')
  81. cmdTextList += get_text_list_for_frame(frame)
  82. cmdTextList.append('</xml>')
  83. text = ''.join(cmdTextList)
  84. if dbg.writer is not None:
  85. dbg.writer.add_command(NetCommand(145, 0, text))
  86. def log_new_thread(global_debugger):
  87. t = threadingCurrentThread()
  88. event_time = cur_time() - global_debugger.thread_analyser.start_time
  89. send_message("threading_event", event_time, t.getName(), get_thread_id(t), "thread",
  90. "start", "code_name", 0, None, parent=get_thread_id(t))
  91. class ThreadingLogger:
  92. def __init__(self):
  93. self.start_time = cur_time()
  94. def set_start_time(self, time):
  95. self.start_time = time
  96. def log_event(self, frame):
  97. write_log = False
  98. self_obj = None
  99. if dict_contains(frame.f_locals, "self"):
  100. self_obj = frame.f_locals["self"]
  101. if isinstance(self_obj, threading.Thread) or self_obj.__class__ == ObjectWrapper:
  102. write_log = True
  103. if hasattr(frame, "f_back") and frame.f_back is not None:
  104. back = frame.f_back
  105. if hasattr(back, "f_back") and back.f_back is not None:
  106. back = back.f_back
  107. if dict_contains(back.f_locals, "self"):
  108. if isinstance(back.f_locals["self"], threading.Thread):
  109. write_log = True
  110. try:
  111. if write_log:
  112. t = threadingCurrentThread()
  113. back = frame.f_back
  114. if not back:
  115. return
  116. _, name, back_base = pydevd_file_utils.get_abs_path_real_path_and_base_from_frame(back)
  117. event_time = cur_time() - self.start_time
  118. method_name = frame.f_code.co_name
  119. if isinstance(self_obj, threading.Thread):
  120. if not hasattr(self_obj, "_pydev_run_patched"):
  121. wrap_attr(self_obj, "run")
  122. if (method_name in THREAD_METHODS) and (back_base not in DONT_TRACE_THREADING or \
  123. (method_name in INNER_METHODS and back_base in INNER_FILES)):
  124. thread_id = get_thread_id(self_obj)
  125. name = self_obj.getName()
  126. real_method = frame.f_code.co_name
  127. parent = None
  128. if real_method == "_stop":
  129. if back_base in INNER_FILES and \
  130. back.f_code.co_name == "_wait_for_tstate_lock":
  131. back = back.f_back.f_back
  132. real_method = "stop"
  133. if hasattr(self_obj, "_pydev_join_called"):
  134. parent = get_thread_id(t)
  135. elif real_method == "join":
  136. # join called in the current thread, not in self object
  137. if not self_obj.is_alive():
  138. return
  139. thread_id = get_thread_id(t)
  140. name = t.getName()
  141. setattr(self_obj, "_pydev_join_called", True)
  142. if real_method == "start":
  143. parent = get_thread_id(t)
  144. send_message("threading_event", event_time, name, thread_id, "thread",
  145. real_method, back.f_code.co_filename, back.f_lineno, back, parent=parent)
  146. # print(event_time, self_obj.getName(), thread_id, "thread",
  147. # real_method, back.f_code.co_filename, back.f_lineno)
  148. if method_name == "pydev_after_run_call":
  149. if hasattr(frame, "f_back") and frame.f_back is not None:
  150. back = frame.f_back
  151. if hasattr(back, "f_back") and back.f_back is not None:
  152. back = back.f_back
  153. if dict_contains(back.f_locals, "self"):
  154. if isinstance(back.f_locals["self"], threading.Thread):
  155. my_self_obj = frame.f_back.f_back.f_locals["self"]
  156. my_back = frame.f_back.f_back
  157. my_thread_id = get_thread_id(my_self_obj)
  158. send_massage = True
  159. if IS_PY3K and hasattr(my_self_obj, "_pydev_join_called"):
  160. send_massage = False
  161. # we can't detect stop after join in Python 2 yet
  162. if send_massage:
  163. send_message("threading_event", event_time, "Thread", my_thread_id, "thread",
  164. "stop", my_back.f_code.co_filename, my_back.f_lineno, my_back, parent=None)
  165. if self_obj.__class__ == ObjectWrapper:
  166. if back_base in DONT_TRACE_THREADING:
  167. # do not trace methods called from threading
  168. return
  169. back_back_base = pydevd_file_utils.get_abs_path_real_path_and_base_from_frame(back.f_back)[-1]
  170. back = back.f_back
  171. if back_back_base in DONT_TRACE_THREADING:
  172. # back_back_base is the file, where the method was called froms
  173. return
  174. if method_name == "__init__":
  175. send_message("threading_event", event_time, t.getName(), get_thread_id(t), "lock",
  176. method_name, back.f_code.co_filename, back.f_lineno, back, lock_id=str(id(frame.f_locals["self"])))
  177. if dict_contains(frame.f_locals, "attr") and \
  178. (frame.f_locals["attr"] in LOCK_METHODS or
  179. frame.f_locals["attr"] in QUEUE_METHODS):
  180. real_method = frame.f_locals["attr"]
  181. if method_name == "call_begin":
  182. real_method += "_begin"
  183. elif method_name == "call_end":
  184. real_method += "_end"
  185. else:
  186. return
  187. if real_method == "release_end":
  188. # do not log release end. Maybe use it later
  189. return
  190. send_message("threading_event", event_time, t.getName(), get_thread_id(t), "lock",
  191. real_method, back.f_code.co_filename, back.f_lineno, back, lock_id=str(id(self_obj)))
  192. if real_method in ("put_end", "get_end"):
  193. # fake release for queue, cause we don't call it directly
  194. send_message("threading_event", event_time, t.getName(), get_thread_id(t), "lock",
  195. "release", back.f_code.co_filename, back.f_lineno, back, lock_id=str(id(self_obj)))
  196. # print(event_time, t.getName(), get_thread_id(t), "lock",
  197. # real_method, back.f_code.co_filename, back.f_lineno)
  198. except Exception:
  199. traceback.print_exc()
  200. class NameManager:
  201. def __init__(self, name_prefix):
  202. self.tasks = {}
  203. self.last = 0
  204. self.prefix = name_prefix
  205. def get(self, id):
  206. if id not in self.tasks:
  207. self.last += 1
  208. self.tasks[id] = self.prefix + "-" + str(self.last)
  209. return self.tasks[id]
  210. class AsyncioLogger:
  211. def __init__(self):
  212. self.task_mgr = NameManager("Task")
  213. self.coro_mgr = NameManager("Coro")
  214. self.start_time = cur_time()
  215. def get_task_id(self, frame):
  216. while frame is not None:
  217. if dict_contains(frame.f_locals, "self"):
  218. self_obj = frame.f_locals["self"]
  219. if isinstance(self_obj, asyncio.Task):
  220. method_name = frame.f_code.co_name
  221. if method_name == "_step":
  222. return id(self_obj)
  223. frame = frame.f_back
  224. return None
  225. def log_event(self, frame):
  226. event_time = cur_time() - self.start_time
  227. # Debug loop iterations
  228. # if isinstance(self_obj, asyncio.base_events.BaseEventLoop):
  229. # if method_name == "_run_once":
  230. # print("Loop iteration")
  231. if not hasattr(frame, "f_back") or frame.f_back is None:
  232. return
  233. back = frame.f_back
  234. if dict_contains(frame.f_locals, "self"):
  235. self_obj = frame.f_locals["self"]
  236. if isinstance(self_obj, asyncio.Task):
  237. method_name = frame.f_code.co_name
  238. if method_name == "set_result":
  239. task_id = id(self_obj)
  240. task_name = self.task_mgr.get(str(task_id))
  241. send_message("asyncio_event", event_time, task_name, task_name, "thread", "stop", frame.f_code.co_filename,
  242. frame.f_lineno, frame)
  243. method_name = back.f_code.co_name
  244. if method_name == "__init__":
  245. task_id = id(self_obj)
  246. task_name = self.task_mgr.get(str(task_id))
  247. send_message("asyncio_event", event_time, task_name, task_name, "thread", "start", frame.f_code.co_filename,
  248. frame.f_lineno, frame)
  249. method_name = frame.f_code.co_name
  250. if isinstance(self_obj, asyncio.Lock):
  251. if method_name in ("acquire", "release"):
  252. task_id = self.get_task_id(frame)
  253. task_name = self.task_mgr.get(str(task_id))
  254. if method_name == "acquire":
  255. if not self_obj._waiters and not self_obj.locked():
  256. send_message("asyncio_event", event_time, task_name, task_name, "lock",
  257. method_name+"_begin", frame.f_code.co_filename, frame.f_lineno, frame, lock_id=str(id(self_obj)))
  258. if self_obj.locked():
  259. method_name += "_begin"
  260. else:
  261. method_name += "_end"
  262. elif method_name == "release":
  263. method_name += "_end"
  264. send_message("asyncio_event", event_time, task_name, task_name, "lock",
  265. method_name, frame.f_code.co_filename, frame.f_lineno, frame, lock_id=str(id(self_obj)))
  266. if isinstance(self_obj, asyncio.Queue):
  267. if method_name in ("put", "get", "_put", "_get"):
  268. task_id = self.get_task_id(frame)
  269. task_name = self.task_mgr.get(str(task_id))
  270. if method_name == "put":
  271. send_message("asyncio_event", event_time, task_name, task_name, "lock",
  272. "acquire_begin", frame.f_code.co_filename, frame.f_lineno, frame, lock_id=str(id(self_obj)))
  273. elif method_name == "_put":
  274. send_message("asyncio_event", event_time, task_name, task_name, "lock",
  275. "acquire_end", frame.f_code.co_filename, frame.f_lineno, frame, lock_id=str(id(self_obj)))
  276. send_message("asyncio_event", event_time, task_name, task_name, "lock",
  277. "release", frame.f_code.co_filename, frame.f_lineno, frame, lock_id=str(id(self_obj)))
  278. elif method_name == "get":
  279. back = frame.f_back
  280. if back.f_code.co_name != "send":
  281. send_message("asyncio_event", event_time, task_name, task_name, "lock",
  282. "acquire_begin", frame.f_code.co_filename, frame.f_lineno, frame, lock_id=str(id(self_obj)))
  283. else:
  284. send_message("asyncio_event", event_time, task_name, task_name, "lock",
  285. "acquire_end", frame.f_code.co_filename, frame.f_lineno, frame, lock_id=str(id(self_obj)))
  286. send_message("asyncio_event", event_time, task_name, task_name, "lock",
  287. "release", frame.f_code.co_filename, frame.f_lineno, frame, lock_id=str(id(self_obj)))