PageRenderTime 27ms CodeModel.GetById 12ms RepoModel.GetById 0ms app.codeStats 0ms

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

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