| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 1 | import os |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 2 | import sys |
| 3 | import pydev_log |
| 4 | import traceback |
| 5 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 6 | pydev_src_dir = os.path.dirname(__file__) |
| 7 | |
| 8 | from pydevd_constants import xrange |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 9 | |
| 10 | def 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 | |
| 20 | def 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 Norbye | 8668e1b | 2013-12-20 09:14:04 -0800 | [diff] [blame] | 41 | new_args.extend(args) |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 42 | 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 Norbye | 8668e1b | 2013-12-20 09:14:04 -0800 | [diff] [blame] | 45 | return new_args |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 46 | 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 Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 58 | i += 1 |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 59 | |
| 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 Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 65 | arg = '"%s"' % x |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 66 | 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 Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 74 | i += 1 |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 75 | |
| 76 | return new_args |
| 77 | except: |
| 78 | traceback.print_exc() |
| 79 | return args |
| 80 | |
| 81 | |
| 82 | def 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 Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 88 | x = x.replace('"', '\\"') |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 89 | quoted_args.append('"%s"' % x) |
| 90 | |
| 91 | return ' '.join(quoted_args) |
| 92 | |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 93 | |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 94 | def 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 Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 176 | |
| 177 | def patch_arg_str_win(arg_str): |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 178 | args = str_to_args_windows(arg_str) |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 179 | if not is_python(args[0]): |
| 180 | return arg_str |
| Tor Norbye | 8668e1b | 2013-12-20 09:14:04 -0800 | [diff] [blame] | 181 | arg_str = args_to_str(patch_args(args)) |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 182 | pydev_log.debug("New args: %s" % arg_str) |
| Tor Norbye | 8668e1b | 2013-12-20 09:14:04 -0800 | [diff] [blame] | 183 | return arg_str |
| Tor Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 184 | |
| 185 | def 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 | |
| 193 | def monkey_patch_os(funcname, create_func): |
| 194 | monkey_patch_module(os, funcname, create_func) |
| 195 | |
| 196 | |
| 197 | def warn_multiproc(): |
| 198 | import pydev_log |
| 199 | |
| 200 | pydev_log.error_once( |
| Tor Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 201 | "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 Norbye | 3a2425a | 2013-11-04 10:16:08 -0800 | [diff] [blame] | 203 | |
| 204 | |
| 205 | def 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 | |
| 215 | def create_execl(original_name): |
| 216 | def new_execl(path, *args): |
| 217 | ''' |
| 218 | os.execl(path, arg0, arg1, ...) |
| 219 | os.execle(path, arg0, arg1, ..., env) |
| 220 | os.execlp(file, arg0, arg1, ...) |
| 221 | os.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 | |
| 228 | def create_execv(original_name): |
| 229 | def new_execv(path, args): |
| 230 | ''' |
| 231 | os.execv(path, args) |
| 232 | os.execvp(file, args) |
| 233 | ''' |
| 234 | import os |
| 235 | return getattr(os, original_name)(path, patch_args(args)) |
| 236 | return new_execv |
| 237 | |
| 238 | def create_execve(original_name): |
| 239 | """ |
| 240 | os.execve(path, args, env) |
| 241 | os.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 | |
| 249 | def create_spawnl(original_name): |
| 250 | def new_spawnl(mode, path, *args): |
| 251 | ''' |
| 252 | os.spawnl(mode, path, arg0, arg1, ...) |
| 253 | os.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 | |
| 260 | def create_spawnv(original_name): |
| 261 | def new_spawnv(mode, path, args): |
| 262 | ''' |
| 263 | os.spawnv(mode, path, args) |
| 264 | os.spawnvp(mode, file, args) |
| 265 | ''' |
| 266 | import os |
| 267 | return getattr(os, original_name)(mode, path, patch_args(args)) |
| 268 | return new_spawnv |
| 269 | |
| 270 | def create_spawnve(original_name): |
| 271 | """ |
| 272 | os.spawnve(mode, path, args, env) |
| 273 | os.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 | |
| 280 | def create_CreateProcess(original_name): |
| 281 | """ |
| 282 | CreateProcess(*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 | |
| 292 | def create_CreateProcessWarnMultiproc(original_name): |
| 293 | """ |
| 294 | CreateProcess(*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 | |
| 305 | def 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 | |
| 316 | def 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 | |
| 363 | def 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 Norbye | 1aa2e09 | 2014-08-20 17:01:23 -0700 | [diff] [blame^] | 390 | |
| 391 | |
| 392 | |
| 393 | class _NewThreadStartupWithTrace: |
| 394 | |
| 395 | def __init__(self, original_func): |
| 396 | self.original_func = original_func |
| 397 | |
| 398 | def __call__(self, *args, **kwargs): |
| 399 | from pydevd_comm import GetGlobalDebugger |
| 400 | global_debugger = GetGlobalDebugger() |
| 401 | if global_debugger is not None: |
| 402 | global_debugger.SetTrace(global_debugger.trace_dispatch) |
| 403 | |
| 404 | return self.original_func(*args, **kwargs) |
| 405 | |
| 406 | class _NewThreadStartupWithoutTrace: |
| 407 | |
| 408 | def __init__(self, original_func): |
| 409 | self.original_func = original_func |
| 410 | |
| 411 | def __call__(self, *args, **kwargs): |
| 412 | return self.original_func(*args, **kwargs) |
| 413 | |
| 414 | _UseNewThreadStartup = _NewThreadStartupWithTrace |
| 415 | |
| 416 | def _get_threading_modules(): |
| 417 | threading_modules = [] |
| 418 | from _pydev_imps import _pydev_thread |
| 419 | threading_modules.append(_pydev_thread) |
| 420 | try: |
| 421 | import thread as _thread |
| 422 | threading_modules.append(_thread) |
| 423 | except: |
| 424 | import _thread |
| 425 | threading_modules.append(_thread) |
| 426 | return threading_modules |
| 427 | |
| 428 | threading_modules = _get_threading_modules() |
| 429 | |
| 430 | |
| 431 | |
| 432 | def patch_thread_module(thread): |
| 433 | |
| 434 | if getattr(thread, '_original_start_new_thread', None) is None: |
| 435 | _original_start_new_thread = thread._original_start_new_thread = thread.start_new_thread |
| 436 | else: |
| 437 | _original_start_new_thread = thread._original_start_new_thread |
| 438 | |
| 439 | |
| 440 | class ClassWithPydevStartNewThread: |
| 441 | |
| 442 | def pydev_start_new_thread(self, function, args, kwargs={}): |
| 443 | ''' |
| 444 | We need to replace the original thread.start_new_thread with this function so that threads started |
| 445 | through it and not through the threading module are properly traced. |
| 446 | ''' |
| 447 | return _original_start_new_thread(_UseNewThreadStartup(function), args, kwargs) |
| 448 | |
| 449 | # This is a hack for the situation where the thread.start_new_thread is declared inside a class, such as the one below |
| 450 | # class F(object): |
| 451 | # start_new_thread = thread.start_new_thread |
| 452 | # |
| 453 | # def start_it(self): |
| 454 | # self.start_new_thread(self.function, args, kwargs) |
| 455 | # So, if it's an already bound method, calling self.start_new_thread won't really receive a different 'self' -- it |
| 456 | # does work in the default case because in builtins self isn't passed either. |
| 457 | pydev_start_new_thread = ClassWithPydevStartNewThread().pydev_start_new_thread |
| 458 | |
| 459 | try: |
| 460 | # We need to replace the original thread.start_new_thread with this function so that threads started through |
| 461 | # it and not through the threading module are properly traced. |
| 462 | thread.start_new_thread = pydev_start_new_thread |
| 463 | thread.start_new = pydev_start_new_thread |
| 464 | except: |
| 465 | pass |
| 466 | |
| 467 | def patch_thread_modules(): |
| 468 | for t in threading_modules: |
| 469 | patch_thread_module(t) |
| 470 | |
| 471 | def undo_patch_thread_modules(): |
| 472 | for t in threading_modules: |
| 473 | try: |
| 474 | t.start_new_thread = t._original_start_new_thread |
| 475 | except: |
| 476 | pass |
| 477 | |
| 478 | try: |
| 479 | t.start_new = t._original_start_new_thread |
| 480 | except: |
| 481 | pass |
| 482 | |
| 483 | def disable_trace_thread_modules(): |
| 484 | ''' |
| 485 | Can be used to temporarily stop tracing threads created with thread.start_new_thread. |
| 486 | ''' |
| 487 | global _UseNewThreadStartup |
| 488 | _UseNewThreadStartup = _NewThreadStartupWithoutTrace |
| 489 | |
| 490 | |
| 491 | def enable_trace_thread_modules(): |
| 492 | ''' |
| 493 | Can be used to start tracing threads created with thread.start_new_thread again. |
| 494 | ''' |
| 495 | global _UseNewThreadStartup |
| 496 | _UseNewThreadStartup = _NewThreadStartupWithTrace |