blob: d92378ed0ee1ea16cb0e49e9835c5bda6a5dbc70 [file] [log] [blame]
Tor Norbye3a2425a2013-11-04 10:16:08 -08001import os
Tor Norbye3a2425a2013-11-04 10:16:08 -08002import sys
3import pydev_log
4import traceback
5
Tor Norbye1aa2e092014-08-20 17:01:23 -07006pydev_src_dir = os.path.dirname(__file__)
7
8from pydevd_constants import xrange
Tor Norbye3a2425a2013-11-04 10:16:08 -08009
10def is_python(path):
11 if path.endswith("'") or path.endswith('"'):
12 path = path[1:len(path)-1]
13 filename = os.path.basename(path).lower()
14 for name in ['python', 'jython', 'pypy']:
15 if filename.find(name) != -1:
16 return True
17
18 return False
19
20def patch_args(args):
21 try:
22 pydev_log.debug("Patching args: %s"% str(args))
23
24 import sys
25 new_args = []
26 i = 0
27 if len(args) == 0:
28 return args
29
30 if is_python(args[0]):
31 try:
32 indC = args.index('-c')
33 except ValueError:
34 indC = -1
35
36 if indC != -1:
37 import pydevd
38 host, port = pydevd.dispatch()
39
40 if port is not None:
Tor Norbye8668e1b2013-12-20 09:14:04 -080041 new_args.extend(args)
Tor Norbye1aa2e092014-08-20 17:01:23 -070042 new_args[indC + 1] = ("import sys; sys.path.append(r'%s'); import pydevd; "
43 "pydevd.settrace(host='%s', port=%s, suspend=False, trace_only_current_thread=False, patch_multiprocessing=True); %s") % (
44 pydev_src_dir, host, port, args[indC + 1])
Tor Norbye8668e1b2013-12-20 09:14:04 -080045 return new_args
Tor Norbye3a2425a2013-11-04 10:16:08 -080046 else:
47 new_args.append(args[0])
48 else:
49 pydev_log.debug("Process is not python, returning.")
50 return args
51
52 i = 1
53 while i < len(args):
54 if args[i].startswith('-'):
55 new_args.append(args[i])
56 else:
57 break
Tor Norbye1aa2e092014-08-20 17:01:23 -070058 i += 1
Tor Norbye3a2425a2013-11-04 10:16:08 -080059
60 if args[i].endswith('pydevd.py'): #no need to add pydevd twice
61 return args
62
63 for x in sys.original_argv:
64 if sys.platform == "win32" and not x.endswith('"'):
Tor Norbye1aa2e092014-08-20 17:01:23 -070065 arg = '"%s"' % x
Tor Norbye3a2425a2013-11-04 10:16:08 -080066 else:
67 arg = x
68 new_args.append(arg)
69 if x == '--file':
70 break
71
72 while i < len(args):
73 new_args.append(args[i])
Tor Norbye1aa2e092014-08-20 17:01:23 -070074 i += 1
Tor Norbye3a2425a2013-11-04 10:16:08 -080075
76 return new_args
77 except:
78 traceback.print_exc()
79 return args
80
81
82def args_to_str(args):
83 quoted_args = []
84 for x in args:
85 if x.startswith('"') and x.endswith('"'):
86 quoted_args.append(x)
87 else:
Tor Norbye1aa2e092014-08-20 17:01:23 -070088 x = x.replace('"', '\\"')
Tor Norbye3a2425a2013-11-04 10:16:08 -080089 quoted_args.append('"%s"' % x)
90
91 return ' '.join(quoted_args)
92
Tor Norbye3a2425a2013-11-04 10:16:08 -080093
Tor Norbye1aa2e092014-08-20 17:01:23 -070094def str_to_args_windows(args):
95 # see http:#msdn.microsoft.com/en-us/library/a1y7w461.aspx
96 result = []
97
98 DEFAULT = 0
99 ARG = 1
100 IN_DOUBLE_QUOTE = 2
101
102 state = DEFAULT
103 backslashes = 0
104 buf = ''
105
106 args_len = len(args)
107 for i in xrange(args_len):
108 ch = args[i]
109 if (ch == '\\'):
110 backslashes+=1
111 continue
112 elif (backslashes != 0):
113 if ch == '"':
114 while backslashes >= 2:
115 backslashes -= 2
116 buf += '\\'
117 if (backslashes == 1):
118 if (state == DEFAULT):
119 state = ARG
120
121 buf += '"'
122 backslashes = 0
123 continue
124 # else fall through to switch
125 else:
126 # false alarm, treat passed backslashes literally...
127 if (state == DEFAULT):
128 state = ARG
129
130 while backslashes > 0:
131 backslashes-=1
132 buf += '\\'
133 # fall through to switch
134 if ch in (' ', '\t'):
135 if (state == DEFAULT):
136 # skip
137 continue
138 elif (state == ARG):
139 state = DEFAULT
140 result.append(buf)
141 buf = ''
142 continue
143
144 if state in (DEFAULT, ARG):
145 if ch == '"':
146 state = IN_DOUBLE_QUOTE
147 else:
148 state = ARG
149 buf += ch
150
151 elif state == IN_DOUBLE_QUOTE:
152 if ch == '"':
153 if (i + 1 < args_len and args[i + 1] == '"'):
154 # Undocumented feature in Windows:
155 # Two consecutive double quotes inside a double-quoted argument are interpreted as
156 # a single double quote.
157 buf += '"'
158 i+=1
159 elif len(buf) == 0:
160 # empty string on Windows platform. Account for bug in constructor of JDK's java.lang.ProcessImpl.
161 result.append("\"\"")
162 state = DEFAULT
163 else:
164 state = ARG
165 else:
166 buf += ch
167
168 else:
169 raise RuntimeError('Illegal condition')
170
171 if len(buf) > 0 or state != DEFAULT:
172 result.append(buf)
173
174 return result
175
Tor Norbye3a2425a2013-11-04 10:16:08 -0800176
177def patch_arg_str_win(arg_str):
Tor Norbye1aa2e092014-08-20 17:01:23 -0700178 args = str_to_args_windows(arg_str)
Tor Norbye3a2425a2013-11-04 10:16:08 -0800179 if not is_python(args[0]):
180 return arg_str
Tor Norbye8668e1b2013-12-20 09:14:04 -0800181 arg_str = args_to_str(patch_args(args))
Tor Norbye1aa2e092014-08-20 17:01:23 -0700182 pydev_log.debug("New args: %s" % arg_str)
Tor Norbye8668e1b2013-12-20 09:14:04 -0800183 return arg_str
Tor Norbye3a2425a2013-11-04 10:16:08 -0800184
185def monkey_patch_module(module, funcname, create_func):
186 if hasattr(module, funcname):
187 original_name = 'original_' + funcname
188 if not hasattr(module, original_name):
189 setattr(module, original_name, getattr(module, funcname))
190 setattr(module, funcname, create_func(original_name))
191
192
193def monkey_patch_os(funcname, create_func):
194 monkey_patch_module(os, funcname, create_func)
195
196
197def warn_multiproc():
198 import pydev_log
199
200 pydev_log.error_once(
Tor Norbye1aa2e092014-08-20 17:01:23 -0700201 "pydev debugger: New process is launching (breakpoints won't work in the new process).\n"
202 "pydev debugger: To debug that process please enable 'Attach to subprocess automatically while debugging?' option in the debugger settings.\n")
Tor Norbye3a2425a2013-11-04 10:16:08 -0800203
204
205def create_warn_multiproc(original_name):
206
207 def new_warn_multiproc(*args):
208 import os
209
210 warn_multiproc()
211
212 return getattr(os, original_name)(*args)
213 return new_warn_multiproc
214
215def create_execl(original_name):
216 def new_execl(path, *args):
217 '''
218os.execl(path, arg0, arg1, ...)
219os.execle(path, arg0, arg1, ..., env)
220os.execlp(file, arg0, arg1, ...)
221os.execlpe(file, arg0, arg1, ..., env)
222 '''
223 import os
224 args = patch_args(args)
225 return getattr(os, original_name)(path, *args)
226 return new_execl
227
228def create_execv(original_name):
229 def new_execv(path, args):
230 '''
231os.execv(path, args)
232os.execvp(file, args)
233 '''
234 import os
235 return getattr(os, original_name)(path, patch_args(args))
236 return new_execv
237
238def create_execve(original_name):
239 """
240os.execve(path, args, env)
241os.execvpe(file, args, env)
242 """
243 def new_execve(path, args, env):
244 import os
245 return getattr(os, original_name)(path, patch_args(args), env)
246 return new_execve
247
248
249def create_spawnl(original_name):
250 def new_spawnl(mode, path, *args):
251 '''
252os.spawnl(mode, path, arg0, arg1, ...)
253os.spawnlp(mode, file, arg0, arg1, ...)
254 '''
255 import os
256 args = patch_args(args)
257 return getattr(os, original_name)(mode, path, *args)
258 return new_spawnl
259
260def create_spawnv(original_name):
261 def new_spawnv(mode, path, args):
262 '''
263os.spawnv(mode, path, args)
264os.spawnvp(mode, file, args)
265 '''
266 import os
267 return getattr(os, original_name)(mode, path, patch_args(args))
268 return new_spawnv
269
270def create_spawnve(original_name):
271 """
272os.spawnve(mode, path, args, env)
273os.spawnvpe(mode, file, args, env)
274 """
275 def new_spawnve(mode, path, args, env):
276 import os
277 return getattr(os, original_name)(mode, path, patch_args(args), env)
278 return new_spawnve
279
280def create_CreateProcess(original_name):
281 """
282CreateProcess(*args, **kwargs)
283 """
284 def new_CreateProcess(appName, commandLine, *args):
285 try:
286 import _subprocess
287 except ImportError:
288 import _winapi as _subprocess
289 return getattr(_subprocess, original_name)(appName, patch_arg_str_win(commandLine), *args)
290 return new_CreateProcess
291
292def create_CreateProcessWarnMultiproc(original_name):
293 """
294CreateProcess(*args, **kwargs)
295 """
296 def new_CreateProcess(*args):
297 try:
298 import _subprocess
299 except ImportError:
300 import _winapi as _subprocess
301 warn_multiproc()
302 return getattr(_subprocess, original_name)(*args)
303 return new_CreateProcess
304
305def create_fork(original_name):
306 def new_fork():
307 import os
308 child_process = getattr(os, original_name)() # fork
309 if not child_process:
310 import pydevd
311
312 pydevd.settrace_forked()
313 return child_process
314 return new_fork
315
316def patch_new_process_functions():
317#os.execl(path, arg0, arg1, ...)
318#os.execle(path, arg0, arg1, ..., env)
319#os.execlp(file, arg0, arg1, ...)
320#os.execlpe(file, arg0, arg1, ..., env)
321#os.execv(path, args)
322#os.execve(path, args, env)
323#os.execvp(file, args)
324#os.execvpe(file, args, env)
325 monkey_patch_os('execl', create_execl)
326 monkey_patch_os('execle', create_execl)
327 monkey_patch_os('execlp', create_execl)
328 monkey_patch_os('execlpe', create_execl)
329 monkey_patch_os('execv', create_execv)
330 monkey_patch_os('execve', create_execve)
331 monkey_patch_os('execvp', create_execv)
332 monkey_patch_os('execvpe', create_execve)
333
334#os.spawnl(mode, path, ...)
335#os.spawnle(mode, path, ..., env)
336#os.spawnlp(mode, file, ...)
337#os.spawnlpe(mode, file, ..., env)
338#os.spawnv(mode, path, args)
339#os.spawnve(mode, path, args, env)
340#os.spawnvp(mode, file, args)
341#os.spawnvpe(mode, file, args, env)
342
343 monkey_patch_os('spawnl', create_spawnl)
344 monkey_patch_os('spawnle', create_spawnl)
345 monkey_patch_os('spawnlp', create_spawnl)
346 monkey_patch_os('spawnlpe', create_spawnl)
347 monkey_patch_os('spawnv', create_spawnv)
348 monkey_patch_os('spawnve', create_spawnve)
349 monkey_patch_os('spawnvp', create_spawnv)
350 monkey_patch_os('spawnvpe', create_spawnve)
351
352 if sys.platform != 'win32':
353 monkey_patch_os('fork', create_fork)
354 else:
355 #Windows
356 try:
357 import _subprocess
358 except ImportError:
359 import _winapi as _subprocess
360 monkey_patch_module(_subprocess, 'CreateProcess', create_CreateProcess)
361
362
363def patch_new_process_functions_with_warning():
364 monkey_patch_os('execl', create_warn_multiproc)
365 monkey_patch_os('execle', create_warn_multiproc)
366 monkey_patch_os('execlp', create_warn_multiproc)
367 monkey_patch_os('execlpe', create_warn_multiproc)
368 monkey_patch_os('execv', create_warn_multiproc)
369 monkey_patch_os('execve', create_warn_multiproc)
370 monkey_patch_os('execvp', create_warn_multiproc)
371 monkey_patch_os('execvpe', create_warn_multiproc)
372 monkey_patch_os('spawnl', create_warn_multiproc)
373 monkey_patch_os('spawnle', create_warn_multiproc)
374 monkey_patch_os('spawnlp', create_warn_multiproc)
375 monkey_patch_os('spawnlpe', create_warn_multiproc)
376 monkey_patch_os('spawnv', create_warn_multiproc)
377 monkey_patch_os('spawnve', create_warn_multiproc)
378 monkey_patch_os('spawnvp', create_warn_multiproc)
379 monkey_patch_os('spawnvpe', create_warn_multiproc)
380
381 if sys.platform != 'win32':
382 monkey_patch_os('fork', create_warn_multiproc)
383 else:
384 #Windows
385 try:
386 import _subprocess
387 except ImportError:
388 import _winapi as _subprocess
389 monkey_patch_module(_subprocess, 'CreateProcess', create_CreateProcessWarnMultiproc)
Tor Norbye1aa2e092014-08-20 17:01:23 -0700390
391
392
393class _NewThreadStartupWithTrace:
394
Tor Norbyec3d3a902014-09-04 13:24:04 -0700395 def __init__(self, original_func, args, kwargs):
Tor Norbye1aa2e092014-08-20 17:01:23 -0700396 self.original_func = original_func
Tor Norbyec3d3a902014-09-04 13:24:04 -0700397 self.args = args
398 self.kwargs = kwargs
Tor Norbye1aa2e092014-08-20 17:01:23 -0700399
Tor Norbyec3d3a902014-09-04 13:24:04 -0700400 def __call__(self):
Tor Norbye1aa2e092014-08-20 17:01:23 -0700401 from pydevd_comm import GetGlobalDebugger
402 global_debugger = GetGlobalDebugger()
403 if global_debugger is not None:
404 global_debugger.SetTrace(global_debugger.trace_dispatch)
405
Tor Norbyec3d3a902014-09-04 13:24:04 -0700406 return self.original_func(*self.args, **self.kwargs)
Tor Norbye1aa2e092014-08-20 17:01:23 -0700407
408class _NewThreadStartupWithoutTrace:
409
Tor Norbyec3d3a902014-09-04 13:24:04 -0700410 def __init__(self, original_func, args, kwargs):
Tor Norbye1aa2e092014-08-20 17:01:23 -0700411 self.original_func = original_func
Tor Norbyec3d3a902014-09-04 13:24:04 -0700412 self.args = args
413 self.kwargs = kwargs
Tor Norbye1aa2e092014-08-20 17:01:23 -0700414
Tor Norbyec3d3a902014-09-04 13:24:04 -0700415 def __call__(self):
416 return self.original_func(*self.args, **self.kwargs)
Tor Norbye1aa2e092014-08-20 17:01:23 -0700417
418_UseNewThreadStartup = _NewThreadStartupWithTrace
419
Tor Norbyec3d3a902014-09-04 13:24:04 -0700420def _get_threading_modules_to_patch():
421 threading_modules_to_patch = []
Tor Norbye1aa2e092014-08-20 17:01:23 -0700422 try:
423 import thread as _thread
Tor Norbyec3d3a902014-09-04 13:24:04 -0700424 threading_modules_to_patch.append(_thread)
Tor Norbye1aa2e092014-08-20 17:01:23 -0700425 except:
426 import _thread
Tor Norbyec3d3a902014-09-04 13:24:04 -0700427 threading_modules_to_patch.append(_thread)
428 return threading_modules_to_patch
Tor Norbye1aa2e092014-08-20 17:01:23 -0700429
Tor Norbyec3d3a902014-09-04 13:24:04 -0700430threading_modules_to_patch = _get_threading_modules_to_patch()
Tor Norbye1aa2e092014-08-20 17:01:23 -0700431
432
433
434def patch_thread_module(thread):
435
436 if getattr(thread, '_original_start_new_thread', None) is None:
437 _original_start_new_thread = thread._original_start_new_thread = thread.start_new_thread
438 else:
439 _original_start_new_thread = thread._original_start_new_thread
440
441
442 class ClassWithPydevStartNewThread:
443
Tor Norbyec3d3a902014-09-04 13:24:04 -0700444 def pydev_start_new_thread(self, function, args=(), kwargs={}):
Tor Norbye1aa2e092014-08-20 17:01:23 -0700445 '''
446 We need to replace the original thread.start_new_thread with this function so that threads started
447 through it and not through the threading module are properly traced.
448 '''
Tor Norbyec3d3a902014-09-04 13:24:04 -0700449 return _original_start_new_thread(_UseNewThreadStartup(function, args, kwargs), ())
Tor Norbye1aa2e092014-08-20 17:01:23 -0700450
451 # This is a hack for the situation where the thread.start_new_thread is declared inside a class, such as the one below
452 # class F(object):
453 # start_new_thread = thread.start_new_thread
454 #
455 # def start_it(self):
456 # self.start_new_thread(self.function, args, kwargs)
457 # So, if it's an already bound method, calling self.start_new_thread won't really receive a different 'self' -- it
458 # does work in the default case because in builtins self isn't passed either.
459 pydev_start_new_thread = ClassWithPydevStartNewThread().pydev_start_new_thread
460
461 try:
462 # We need to replace the original thread.start_new_thread with this function so that threads started through
463 # it and not through the threading module are properly traced.
464 thread.start_new_thread = pydev_start_new_thread
465 thread.start_new = pydev_start_new_thread
466 except:
467 pass
468
469def patch_thread_modules():
Tor Norbyec3d3a902014-09-04 13:24:04 -0700470 for t in threading_modules_to_patch:
Tor Norbye1aa2e092014-08-20 17:01:23 -0700471 patch_thread_module(t)
472
473def undo_patch_thread_modules():
Tor Norbyec3d3a902014-09-04 13:24:04 -0700474 for t in threading_modules_to_patch:
Tor Norbye1aa2e092014-08-20 17:01:23 -0700475 try:
476 t.start_new_thread = t._original_start_new_thread
477 except:
478 pass
479
480 try:
481 t.start_new = t._original_start_new_thread
482 except:
483 pass
484
485def disable_trace_thread_modules():
486 '''
487 Can be used to temporarily stop tracing threads created with thread.start_new_thread.
488 '''
489 global _UseNewThreadStartup
490 _UseNewThreadStartup = _NewThreadStartupWithoutTrace
491
492
493def enable_trace_thread_modules():
494 '''
495 Can be used to start tracing threads created with thread.start_new_thread again.
496 '''
497 global _UseNewThreadStartup
498 _UseNewThreadStartup = _NewThreadStartupWithTrace
Tor Norbyec3d3a902014-09-04 13:24:04 -0700499
500def get_original_start_new_thread(threading_module):
501 try:
502 return threading_module._original_start_new_thread
503 except:
504 return threading_module.start_new_thread