mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-16 22:51:17 +07:00
(cherry picked from commit f6f7f0ffed6cd2d51410e9fe07267f813b4c4ff1) IJ-MR-22034 GitOrigin-RevId: eff02449dcab685184cb9c7d4e86fb9766fb5196
359 lines
17 KiB
Python
359 lines
17 KiB
Python
import pydevd_file_utils
|
|
from _pydev_bundle._pydev_filesystem_encoding import getfilesystemencoding
|
|
from _pydevd_bundle import pydevd_xml
|
|
from _pydevd_bundle.pydevd_constants import get_thread_id, IS_PY3K, IS_PY36_OR_GREATER
|
|
from pydevd_concurrency_analyser.pydevd_thread_wrappers import ObjectWrapper, AsyncioTaskWrapper, wrap_attr
|
|
|
|
file_system_encoding = getfilesystemencoding()
|
|
|
|
try:
|
|
from urllib import quote
|
|
except:
|
|
from urllib.parse import quote # @UnresolvedImport
|
|
|
|
from _pydev_imps._pydev_saved_modules import threading
|
|
threadingCurrentThread = threading.current_thread
|
|
|
|
|
|
DONT_TRACE_THREADING = ['threading.py', 'pydevd.py']
|
|
INNER_METHODS = ['_stop']
|
|
INNER_FILES = ['threading.py']
|
|
QUEUE_MODULE = 'queue.py'
|
|
# Tread method `start` is removed, because it's being handled in `pydev_monkey` thread creation patching
|
|
THREAD_METHODS = ['_stop', 'join']
|
|
LOCK_METHODS = ['__init__', 'acquire', 'release', '__enter__', '__exit__']
|
|
QUEUE_METHODS = ['put', 'get']
|
|
ALL_METHODS = LOCK_METHODS + QUEUE_METHODS
|
|
|
|
from _pydevd_bundle.pydevd_comm import NetCommand
|
|
from _pydevd_bundle.pydevd_constants import GlobalDebuggerHolder
|
|
import traceback
|
|
|
|
import time
|
|
# return time since epoch in milliseconds
|
|
cur_time = lambda: int(round(time.time() * 1000000))
|
|
|
|
|
|
try:
|
|
import asyncio # @UnresolvedImport
|
|
except:
|
|
pass
|
|
|
|
|
|
def get_text_list_for_frame(frame):
|
|
# partial copy-paste from make_thread_suspend_str
|
|
curFrame = frame
|
|
cmdTextList = []
|
|
try:
|
|
while curFrame:
|
|
#print cmdText
|
|
myId = str(id(curFrame))
|
|
#print "id is ", myId
|
|
|
|
if curFrame.f_code is None:
|
|
break #Iron Python sometimes does not have it!
|
|
|
|
myName = curFrame.f_code.co_name #method name (if in method) or ? if global
|
|
if myName is None:
|
|
break #Iron Python sometimes does not have it!
|
|
|
|
#print "name is ", myName
|
|
|
|
filename = pydevd_file_utils.get_abs_path_real_path_and_base_from_frame(curFrame)[1]
|
|
|
|
myFile = pydevd_file_utils.norm_file_to_client(filename)
|
|
if file_system_encoding.lower() != "utf-8" and hasattr(myFile, "decode"):
|
|
# myFile is a byte string encoded using the file system encoding
|
|
# convert it to utf8
|
|
myFile = myFile.decode(file_system_encoding).encode("utf-8")
|
|
|
|
#print "file is ", myFile
|
|
#myFile = inspect.getsourcefile(curFrame) or inspect.getfile(frame)
|
|
|
|
myLine = str(curFrame.f_lineno)
|
|
#print "line is ", myLine
|
|
|
|
#the variables are all gotten 'on-demand'
|
|
#variables = pydevd_xml.frame_vars_to_xml(curFrame.f_locals)
|
|
|
|
variables = ''
|
|
cmdTextList.append('<frame id="%s" name="%s" ' % (myId , pydevd_xml.make_valid_xml_value(myName)))
|
|
cmdTextList.append('file="%s" line="%s">' % (quote(myFile, '/>_= \t'), myLine))
|
|
cmdTextList.append(variables)
|
|
cmdTextList.append("</frame>")
|
|
curFrame = curFrame.f_back
|
|
except :
|
|
traceback.print_exc()
|
|
|
|
return cmdTextList
|
|
|
|
|
|
def send_message(event_class, time, name, thread_id, type, event, file, line, frame, lock_id=0, parent=None):
|
|
dbg = GlobalDebuggerHolder.global_dbg
|
|
if dbg is None:
|
|
return
|
|
cmdTextList = ['<xml>']
|
|
|
|
cmdTextList.append('<' + event_class)
|
|
cmdTextList.append(' time="%s"' % pydevd_xml.make_valid_xml_value(str(time)))
|
|
cmdTextList.append(' name="%s"' % pydevd_xml.make_valid_xml_value(name))
|
|
cmdTextList.append(' thread_id="%s"' % pydevd_xml.make_valid_xml_value(thread_id))
|
|
cmdTextList.append(' type="%s"' % pydevd_xml.make_valid_xml_value(type))
|
|
if type == "lock":
|
|
cmdTextList.append(' lock_id="%s"' % pydevd_xml.make_valid_xml_value(str(lock_id)))
|
|
if parent is not None:
|
|
cmdTextList.append(' parent="%s"' % pydevd_xml.make_valid_xml_value(parent))
|
|
cmdTextList.append(' event="%s"' % pydevd_xml.make_valid_xml_value(event))
|
|
cmdTextList.append(' file="%s"' % pydevd_xml.make_valid_xml_value(file))
|
|
cmdTextList.append(' line="%s"' % pydevd_xml.make_valid_xml_value(str(line)))
|
|
cmdTextList.append('></' + event_class + '>')
|
|
|
|
cmdTextList += get_text_list_for_frame(frame)
|
|
cmdTextList.append('</xml>')
|
|
|
|
text = ''.join(cmdTextList)
|
|
if dbg.writer is not None:
|
|
dbg.writer.add_command(NetCommand(145, 0, text))
|
|
|
|
|
|
def log_new_thread(global_debugger, t):
|
|
event_time = cur_time() - global_debugger.thread_analyser.start_time
|
|
send_message("threading_event", event_time, t.getName(), get_thread_id(t), "thread",
|
|
"start", "code_name", 0, None, parent=get_thread_id(t))
|
|
|
|
|
|
class ThreadingLogger:
|
|
def __init__(self):
|
|
self.start_time = cur_time()
|
|
|
|
def set_start_time(self, time):
|
|
self.start_time = time
|
|
|
|
def log_event(self, frame):
|
|
write_log = False
|
|
self_obj = None
|
|
if "self" in frame.f_locals:
|
|
self_obj = frame.f_locals["self"]
|
|
if isinstance(self_obj, threading.Thread) or self_obj.__class__ == ObjectWrapper:
|
|
write_log = True
|
|
if hasattr(frame, "f_back") and frame.f_back is not None:
|
|
back = frame.f_back
|
|
if hasattr(back, "f_back") and back.f_back is not None:
|
|
back = back.f_back
|
|
if "self" in back.f_locals:
|
|
if isinstance(back.f_locals["self"], threading.Thread):
|
|
write_log = True
|
|
try:
|
|
if write_log:
|
|
t = threadingCurrentThread()
|
|
back = frame.f_back
|
|
if not back:
|
|
return
|
|
_, name, back_base = pydevd_file_utils.get_abs_path_real_path_and_base_from_frame(back)
|
|
event_time = cur_time() - self.start_time
|
|
method_name = frame.f_code.co_name
|
|
|
|
if isinstance(self_obj, threading.Thread):
|
|
if not hasattr(self_obj, "_pydev_run_patched"):
|
|
wrap_attr(self_obj, "run")
|
|
if (method_name in THREAD_METHODS) and (back_base not in DONT_TRACE_THREADING or \
|
|
(method_name in INNER_METHODS and back_base in INNER_FILES)):
|
|
thread_id = get_thread_id(self_obj)
|
|
name = self_obj.getName()
|
|
real_method = frame.f_code.co_name
|
|
parent = None
|
|
if real_method == "_stop":
|
|
if back_base in INNER_FILES and \
|
|
back.f_code.co_name == "_wait_for_tstate_lock":
|
|
back = back.f_back.f_back
|
|
real_method = "stop"
|
|
if hasattr(self_obj, "_pydev_join_called"):
|
|
parent = get_thread_id(t)
|
|
elif real_method == "join":
|
|
# join called in the current thread, not in self object
|
|
if not self_obj.is_alive():
|
|
return
|
|
thread_id = get_thread_id(t)
|
|
name = t.getName()
|
|
self_obj._pydev_join_called = True
|
|
|
|
send_message("threading_event", event_time, name, thread_id, "thread",
|
|
real_method, back.f_code.co_filename, back.f_lineno, back, parent=parent)
|
|
# print(event_time, self_obj.getName(), thread_id, "thread",
|
|
# real_method, back.f_code.co_filename, back.f_lineno)
|
|
|
|
if method_name == "pydev_after_run_call":
|
|
if hasattr(frame, "f_back") and frame.f_back is not None:
|
|
back = frame.f_back
|
|
if hasattr(back, "f_back") and back.f_back is not None:
|
|
back = back.f_back
|
|
if "self" in back.f_locals:
|
|
if isinstance(back.f_locals["self"], threading.Thread):
|
|
my_self_obj = frame.f_back.f_back.f_locals["self"]
|
|
my_back = frame.f_back.f_back
|
|
my_thread_id = get_thread_id(my_self_obj)
|
|
send_massage = True
|
|
if IS_PY3K and hasattr(my_self_obj, "_pydev_join_called"):
|
|
send_massage = False
|
|
# we can't detect stop after join in Python 2 yet
|
|
if send_massage:
|
|
send_message("threading_event", event_time, "Thread", my_thread_id, "thread",
|
|
"stop", my_back.f_code.co_filename, my_back.f_lineno, my_back, parent=None)
|
|
|
|
if isinstance(self_obj, ObjectWrapper):
|
|
if back_base in DONT_TRACE_THREADING:
|
|
# do not trace methods called from threading
|
|
return
|
|
back_back_base = pydevd_file_utils.get_abs_path_real_path_and_base_from_frame(back.f_back)[-1]
|
|
bbb_base = None
|
|
bbb_frame = getattr(back.f_back, "f_back", None)
|
|
if bbb_frame is not None:
|
|
bbb_base = pydevd_file_utils.get_abs_path_real_path_and_base_from_frame(bbb_frame)[-1]
|
|
if back_back_base in DONT_TRACE_THREADING and bbb_base is not None and bbb_base != QUEUE_MODULE:
|
|
# back_back_base is the file, where the method was called froms
|
|
return
|
|
back = back.f_back
|
|
|
|
if method_name == "__init__":
|
|
send_message("threading_event", event_time, t.getName(), get_thread_id(t), "lock",
|
|
method_name, back.f_code.co_filename, back.f_lineno, back, lock_id=str(id(frame.f_locals["self"])))
|
|
if "attr" in frame.f_locals:
|
|
real_method = frame.f_locals["attr"]
|
|
if real_method not in ALL_METHODS:
|
|
return
|
|
if method_name == "call_begin":
|
|
real_method += "_begin"
|
|
elif method_name == "call_end":
|
|
real_method += "_end"
|
|
else:
|
|
return
|
|
if real_method == "release_end":
|
|
# do not log release end. Maybe use it later
|
|
return
|
|
send_message("threading_event", event_time, t.getName(), get_thread_id(t), "lock",
|
|
real_method, back.f_code.co_filename, back.f_lineno, back, lock_id=str(id(self_obj)))
|
|
except Exception:
|
|
traceback.print_exc()
|
|
|
|
|
|
class NameManager:
|
|
def __init__(self, name_prefix):
|
|
self.tasks = {}
|
|
self.last = 0
|
|
self.prefix = name_prefix
|
|
|
|
def get(self, id):
|
|
if id not in self.tasks:
|
|
self.last += 1
|
|
self.tasks[id] = self.prefix + "-" + str(self.last)
|
|
return self.tasks[id]
|
|
|
|
|
|
class AsyncioLogger:
|
|
def __init__(self):
|
|
self.task_mgr = NameManager("Task")
|
|
self.start_time = cur_time()
|
|
|
|
def get_task_id(self, frame):
|
|
if IS_PY36_OR_GREATER:
|
|
if "self" in frame.f_locals:
|
|
self_obj = frame.f_locals["self"]
|
|
current_task = asyncio.tasks._OrigTask.current_task(self_obj._loop)
|
|
return id(current_task)
|
|
else:
|
|
while frame is not None:
|
|
if "self" in frame.f_locals:
|
|
self_obj = frame.f_locals["self"]
|
|
if isinstance(self_obj, asyncio.Task):
|
|
method_name = frame.f_code.co_name
|
|
if method_name == "_step":
|
|
return id(self_obj)
|
|
frame = frame.f_back
|
|
|
|
def log_event(self, frame):
|
|
event_time = cur_time() - self.start_time
|
|
|
|
if not hasattr(frame, "f_back") or frame.f_back is None:
|
|
return
|
|
back = frame.f_back
|
|
|
|
if "self" in frame.f_locals:
|
|
self_obj = frame.f_locals["self"]
|
|
if IS_PY36_OR_GREATER:
|
|
if self_obj.__class__ == AsyncioTaskWrapper:
|
|
method_name = frame.f_code.co_name
|
|
if method_name == "__init__" and "obj" in frame.f_locals:
|
|
original_task = frame.f_locals["obj"]
|
|
task_id = id(original_task)
|
|
task_name = self.task_mgr.get(str(task_id))
|
|
if hasattr(frame, "f_back") and hasattr(frame.f_back, "f_back"):
|
|
frame = frame.f_back.f_back
|
|
send_message("asyncio_event", event_time, task_name, task_name, "thread", "start", frame.f_code.co_filename,
|
|
frame.f_lineno, frame)
|
|
if method_name == "call_end" and "attr" in frame.f_locals:
|
|
real_method = frame.f_locals["attr"]
|
|
if real_method == "done":
|
|
task_id = id(self_obj.wrapped_object)
|
|
task_name = self.task_mgr.get(str(task_id))
|
|
send_message("asyncio_event", event_time, task_name, task_name, "thread", "stop", frame.f_code.co_filename,
|
|
frame.f_lineno, frame)
|
|
else:
|
|
if isinstance(self_obj, asyncio.Task):
|
|
method_name = frame.f_code.co_name
|
|
if method_name == "set_result":
|
|
task_id = id(self_obj)
|
|
task_name = self.task_mgr.get(str(task_id))
|
|
send_message("asyncio_event", event_time, task_name, task_name, "thread", "stop", frame.f_code.co_filename,
|
|
frame.f_lineno, frame)
|
|
|
|
method_name = back.f_code.co_name
|
|
if method_name == "__init__":
|
|
task_id = id(self_obj)
|
|
task_name = self.task_mgr.get(str(task_id))
|
|
send_message("asyncio_event", event_time, task_name, task_name, "thread", "start", frame.f_code.co_filename,
|
|
frame.f_lineno, frame)
|
|
|
|
method_name = frame.f_code.co_name
|
|
if isinstance(self_obj, asyncio.Lock):
|
|
if method_name in ("acquire", "release"):
|
|
task_id = self.get_task_id(frame)
|
|
task_name = self.task_mgr.get(str(task_id))
|
|
|
|
if method_name == "acquire":
|
|
if not self_obj._waiters and not self_obj.locked():
|
|
send_message("asyncio_event", event_time, task_name, task_name, "lock",
|
|
method_name+"_begin", frame.f_code.co_filename, frame.f_lineno, frame, lock_id=str(id(self_obj)))
|
|
if self_obj.locked():
|
|
method_name += "_begin"
|
|
else:
|
|
method_name += "_end"
|
|
elif method_name == "release":
|
|
method_name += "_end"
|
|
|
|
send_message("asyncio_event", event_time, task_name, task_name, "lock",
|
|
method_name, frame.f_code.co_filename, frame.f_lineno, frame, lock_id=str(id(self_obj)))
|
|
|
|
if isinstance(self_obj, asyncio.Queue):
|
|
if method_name in ("put", "get", "_put", "_get"):
|
|
task_id = self.get_task_id(frame)
|
|
task_name = self.task_mgr.get(str(task_id))
|
|
|
|
if method_name == "put":
|
|
send_message("asyncio_event", event_time, task_name, task_name, "lock",
|
|
"acquire_begin", frame.f_code.co_filename, frame.f_lineno, frame, lock_id=str(id(self_obj)))
|
|
elif method_name == "_put":
|
|
send_message("asyncio_event", event_time, task_name, task_name, "lock",
|
|
"acquire_end", frame.f_code.co_filename, frame.f_lineno, frame, lock_id=str(id(self_obj)))
|
|
send_message("asyncio_event", event_time, task_name, task_name, "lock",
|
|
"release", frame.f_code.co_filename, frame.f_lineno, frame, lock_id=str(id(self_obj)))
|
|
elif method_name == "get":
|
|
back = frame.f_back
|
|
if back.f_code.co_name != "send":
|
|
send_message("asyncio_event", event_time, task_name, task_name, "lock",
|
|
"acquire_begin", frame.f_code.co_filename, frame.f_lineno, frame, lock_id=str(id(self_obj)))
|
|
else:
|
|
send_message("asyncio_event", event_time, task_name, task_name, "lock",
|
|
"acquire_end", frame.f_code.co_filename, frame.f_lineno, frame, lock_id=str(id(self_obj)))
|
|
send_message("asyncio_event", event_time, task_name, task_name, "lock",
|
|
"release", frame.f_code.co_filename, frame.f_lineno, frame, lock_id=str(id(self_obj)))
|