1793 lines · python
1"""2This LLDB module contains miscellaneous utilities.3Some of the test suite takes advantage of the utility functions defined here.4They can also be useful for general purpose lldb scripting.5"""6 7# System modules8import errno9import io10import json11import os12import re13import sys14import subprocess15from typing import Dict16 17# LLDB modules18import lldb19from . import lldbtest_config20from . import configuration21 22# How often failed simulator process launches are retried.23SIMULATOR_RETRY = 324 25# ===================================================26# Utilities for locating/checking executable programs27# ===================================================28 29 30def is_exe(fpath):31 """Returns True if fpath is an executable."""32 return os.path.isfile(fpath) and os.access(fpath, os.X_OK)33 34 35def which(program):36 """Returns the full path to a program; None otherwise."""37 fpath, fname = os.path.split(program)38 if fpath:39 if is_exe(program):40 return program41 else:42 for path in os.environ["PATH"].split(os.pathsep):43 exe_file = os.path.join(path, program)44 if is_exe(exe_file):45 return exe_file46 return None47 48 49def mkdir_p(path):50 try:51 os.makedirs(path)52 except OSError as e:53 if e.errno != errno.EEXIST:54 raise55 if not os.path.isdir(path):56 raise OSError(errno.ENOTDIR, "%s is not a directory" % path)57 58 59# ============================60# Dealing with SDK and triples61# ============================62 63 64def get_xcode_sdk(os, env):65 # Respect --apple-sdk <path> if it's specified. If the SDK is simply66 # mounted from some disk image, and not actually installed, this is the67 # only way to use it.68 if configuration.apple_sdk:69 return configuration.apple_sdk70 if os == "ios":71 if env == "simulator":72 return "iphonesimulator"73 if env == "macabi":74 return "macosx"75 return "iphoneos"76 elif os == "tvos":77 if env == "simulator":78 return "appletvsimulator"79 return "appletvos"80 elif os == "watchos":81 if env == "simulator":82 return "watchsimulator"83 return "watchos"84 return os85 86 87def get_xcode_sdk_version(sdk):88 return (89 subprocess.check_output(["xcrun", "--sdk", sdk, "--show-sdk-version"])90 .rstrip()91 .decode("utf-8")92 )93 94 95def get_xcode_sdk_root(sdk):96 return (97 subprocess.check_output(["xcrun", "--sdk", sdk, "--show-sdk-path"])98 .rstrip()99 .decode("utf-8")100 )101 102 103def get_xcode_clang(sdk):104 return (105 subprocess.check_output(["xcrun", "-sdk", sdk, "-f", "clang"])106 .rstrip()107 .decode("utf-8")108 )109 110 111# ===================================================112# Disassembly for an SBFunction or an SBSymbol object113# ===================================================114 115 116def disassemble(target, function_or_symbol):117 """Disassemble the function or symbol given a target.118 119 It returns the disassembly content in a string object.120 """121 buf = io.StringIO()122 insts = function_or_symbol.GetInstructions(target)123 for i in insts:124 print(i, file=buf)125 return buf.getvalue()126 127 128# ==========================================================129# Integer (byte size 1, 2, 4, and 8) to bytearray conversion130# ==========================================================131 132 133def int_to_bytearray(val, bytesize):134 """Utility function to convert an integer into a bytearray.135 136 It returns the bytearray in the little endian format. It is easy to get the137 big endian format, just do ba.reverse() on the returned object.138 """139 import struct140 141 if bytesize == 1:142 return bytearray([val])143 144 # Little endian followed by a format character.145 template = "<%c"146 if bytesize == 2:147 fmt = template % "h"148 elif bytesize == 4:149 fmt = template % "i"150 elif bytesize == 4:151 fmt = template % "q"152 else:153 return None154 155 packed = struct.pack(fmt, val)156 return bytearray(packed)157 158 159def bytearray_to_int(bytes, bytesize):160 """Utility function to convert a bytearray into an integer.161 162 It interprets the bytearray in the little endian format. For a big endian163 bytearray, just do ba.reverse() on the object before passing it in.164 """165 import struct166 167 if bytesize == 1:168 return bytes[0]169 170 # Little endian followed by a format character.171 template = "<%c"172 if bytesize == 2:173 fmt = template % "h"174 elif bytesize == 4:175 fmt = template % "i"176 elif bytesize == 4:177 fmt = template % "q"178 else:179 return None180 181 unpacked = struct.unpack_from(fmt, bytes)182 return unpacked[0]183 184 185# ==============================================================186# Get the description of an lldb object or None if not available187# ==============================================================188def get_description(obj, option=None):189 """Calls lldb_obj.GetDescription() and returns a string, or None.190 191 For SBTarget, SBBreakpointLocation, and SBWatchpoint lldb objects, an extra192 option can be passed in to describe the detailed level of description193 desired:194 o lldb.eDescriptionLevelBrief195 o lldb.eDescriptionLevelFull196 o lldb.eDescriptionLevelVerbose197 """198 method = getattr(obj, "GetDescription")199 if not method:200 return None201 tuple = (lldb.SBTarget, lldb.SBBreakpointLocation, lldb.SBWatchpoint)202 if isinstance(obj, tuple):203 if option is None:204 option = lldb.eDescriptionLevelBrief205 206 stream = lldb.SBStream()207 if option is None:208 success = method(stream)209 else:210 success = method(stream, option)211 if not success:212 return None213 return stream.GetData()214 215 216# =================================================217# Convert some enum value to its string counterpart218# =================================================219 220 221def _enum_names(prefix: str) -> Dict[int, str]:222 """Generate a mapping of enum value to name, for the enum prefix."""223 suffix_start = len(prefix)224 return {225 getattr(lldb, attr): attr[suffix_start:].lower()226 for attr in dir(lldb)227 if attr.startswith(prefix)228 }229 230 231_STATE_NAMES = _enum_names(prefix="eState")232 233 234def state_type_to_str(enum: int) -> str:235 """Returns the stateType string given an enum."""236 name = _STATE_NAMES.get(enum)237 if name:238 return name239 raise Exception(f"Unknown StateType enum: {enum}")240 241 242_STOP_REASON_NAMES = _enum_names(prefix="eStopReason")243 244 245def stop_reason_to_str(enum: int) -> str:246 """Returns the stopReason string given an enum."""247 name = _STOP_REASON_NAMES.get(enum)248 if name:249 return name250 raise Exception(f"Unknown StopReason enum: {enum}")251 252 253_SYMBOL_TYPE_NAMES = _enum_names(prefix="eSymbolType")254 255 256def symbol_type_to_str(enum: int) -> str:257 """Returns the symbolType string given an enum."""258 name = _SYMBOL_TYPE_NAMES.get(enum)259 if name:260 return name261 raise Exception(f"Unknown SymbolType enum: {enum}")262 263 264_VALUE_TYPE_NAMES = _enum_names(prefix="eValueType")265 266 267def value_type_to_str(enum: int) -> str:268 """Returns the valueType string given an enum."""269 name = _VALUE_TYPE_NAMES.get(enum)270 if name:271 return name272 raise Exception(f"Unknown ValueType enum: {enum}")273 274 275# ==================================================276# Get stopped threads due to each stop reason.277# ==================================================278 279 280def sort_stopped_threads(281 process,282 breakpoint_threads=None,283 crashed_threads=None,284 watchpoint_threads=None,285 signal_threads=None,286 exiting_threads=None,287 other_threads=None,288):289 """Fills array *_threads with threads stopped for the corresponding stop290 reason.291 """292 for lst in [293 breakpoint_threads,294 watchpoint_threads,295 signal_threads,296 exiting_threads,297 other_threads,298 ]:299 if lst is not None:300 lst[:] = []301 302 for thread in process:303 dispatched = False304 for reason, list in [305 (lldb.eStopReasonBreakpoint, breakpoint_threads),306 (lldb.eStopReasonException, crashed_threads),307 (lldb.eStopReasonWatchpoint, watchpoint_threads),308 (lldb.eStopReasonSignal, signal_threads),309 (lldb.eStopReasonThreadExiting, exiting_threads),310 (None, other_threads),311 ]:312 if not dispatched and list is not None:313 if thread.GetStopReason() == reason or reason is None:314 list.append(thread)315 dispatched = True316 317 318# ==================================================319# Utility functions for setting breakpoints320# ==================================================321 322 323def run_break_set_by_script(324 test, class_name, extra_options=None, num_expected_locations=1325):326 """Set a scripted breakpoint. Check that it got the right number of locations."""327 test.assertTrue(class_name is not None, "Must pass in a class name.")328 command = "breakpoint set -P " + class_name329 if extra_options is not None:330 command += " " + extra_options331 332 break_results = run_break_set_command(test, command)333 check_breakpoint_result(test, break_results, num_locations=num_expected_locations)334 return get_bpno_from_match(break_results)335 336 337def run_break_set_by_file_and_line(338 test,339 file_name,340 line_number,341 extra_options=None,342 num_expected_locations=1,343 loc_exact=False,344 module_name=None,345):346 """Set a breakpoint by file and line, returning the breakpoint number.347 348 If extra_options is not None, then we append it to the breakpoint set command.349 350 If num_expected_locations is -1, we check that we got AT LEAST one location. If num_expected_locations is -2, we don't351 check the actual number at all. Otherwise, we check that num_expected_locations equals the number of locations.352 353 If loc_exact is true, we check that there is one location, and that location must be at the input file and line number.354 """355 356 if file_name is None:357 command = "breakpoint set -l %d" % (line_number)358 else:359 command = 'breakpoint set -f "%s" -l %d' % (file_name, line_number)360 361 if module_name:362 command += " --shlib '%s'" % (module_name)363 364 if extra_options:365 command += " " + extra_options366 367 break_results = run_break_set_command(test, command)368 369 if num_expected_locations == 1 and loc_exact:370 check_breakpoint_result(371 test,372 break_results,373 num_locations=num_expected_locations,374 file_name=file_name,375 line_number=line_number,376 module_name=module_name,377 )378 else:379 check_breakpoint_result(380 test, break_results, num_locations=num_expected_locations381 )382 383 return get_bpno_from_match(break_results)384 385 386def run_break_set_by_symbol(387 test,388 symbol,389 extra_options=None,390 num_expected_locations=-1,391 sym_exact=False,392 module_name=None,393):394 """Set a breakpoint by symbol name. Common options are the same as run_break_set_by_file_and_line.395 396 If sym_exact is true, then the output symbol must match the input exactly, otherwise we do a substring match.397 """398 command = 'breakpoint set -n "%s"' % (symbol)399 400 if module_name:401 command += " --shlib '%s'" % (module_name)402 403 if extra_options:404 command += " " + extra_options405 406 break_results = run_break_set_command(test, command)407 408 if num_expected_locations == 1 and sym_exact:409 check_breakpoint_result(410 test,411 break_results,412 num_locations=num_expected_locations,413 symbol_name=symbol,414 module_name=module_name,415 )416 else:417 check_breakpoint_result(418 test, break_results, num_locations=num_expected_locations419 )420 421 return get_bpno_from_match(break_results)422 423 424def run_break_set_by_selector(425 test, selector, extra_options=None, num_expected_locations=-1, module_name=None426):427 """Set a breakpoint by selector. Common options are the same as run_break_set_by_file_and_line."""428 429 command = 'breakpoint set -S "%s"' % (selector)430 431 if module_name:432 command += ' --shlib "%s"' % (module_name)433 434 if extra_options:435 command += " " + extra_options436 437 break_results = run_break_set_command(test, command)438 439 if num_expected_locations == 1:440 check_breakpoint_result(441 test,442 break_results,443 num_locations=num_expected_locations,444 symbol_name=selector,445 symbol_match_exact=False,446 module_name=module_name,447 )448 else:449 check_breakpoint_result(450 test, break_results, num_locations=num_expected_locations451 )452 453 return get_bpno_from_match(break_results)454 455 456def run_break_set_by_regexp(457 test, regexp, extra_options=None, num_expected_locations=-1458):459 """Set a breakpoint by regular expression match on symbol name. Common options are the same as run_break_set_by_file_and_line."""460 461 command = 'breakpoint set -r "%s"' % (regexp)462 if extra_options:463 command += " " + extra_options464 465 break_results = run_break_set_command(test, command)466 467 check_breakpoint_result(test, break_results, num_locations=num_expected_locations)468 469 return get_bpno_from_match(break_results)470 471 472def run_break_set_by_source_regexp(473 test, regexp, extra_options=None, num_expected_locations=-1474):475 """Set a breakpoint by source regular expression. Common options are the same as run_break_set_by_file_and_line."""476 command = 'breakpoint set -p "%s"' % (regexp)477 if extra_options:478 command += " " + extra_options479 480 break_results = run_break_set_command(test, command)481 482 check_breakpoint_result(test, break_results, num_locations=num_expected_locations)483 484 return get_bpno_from_match(break_results)485 486 487def run_break_set_by_file_colon_line(488 test,489 specifier,490 path,491 line_number,492 column_number=0,493 extra_options=None,494 num_expected_locations=-1,495):496 command = 'breakpoint set -y "%s"' % (specifier)497 if extra_options:498 command += " " + extra_options499 500 print("About to run: '%s'", command)501 break_results = run_break_set_command(test, command)502 check_breakpoint_result(503 test,504 break_results,505 num_locations=num_expected_locations,506 file_name=path,507 line_number=line_number,508 column_number=column_number,509 )510 511 return get_bpno_from_match(break_results)512 513 514def run_break_set_command(test, command):515 """Run the command passed in - it must be some break set variant - and analyze the result.516 Returns a dictionary of information gleaned from the command-line results.517 Will assert if the breakpoint setting fails altogether.518 519 Dictionary will contain:520 bpno - breakpoint of the newly created breakpoint, -1 on error.521 num_locations - number of locations set for the breakpoint.522 523 If there is only one location, the dictionary MAY contain:524 file - source file name525 line_no - source line number526 column - source column number527 symbol - symbol name528 inline_symbol - inlined symbol name529 offset - offset from the original symbol530 module - module531 address - address at which the breakpoint was set."""532 533 patterns = [534 r"^Breakpoint (?P<bpno>[0-9]+): (?P<num_locations>[0-9]+) locations\.$",535 r"^Breakpoint (?P<bpno>[0-9]+): (?P<num_locations>no) locations \(pending\)\.",536 r"^Breakpoint (?P<bpno>[0-9]+): where = (?P<module>.*)`(?P<symbol>[+\-]{0,1}[^+]+)( \+ (?P<offset>[0-9]+)){0,1}( \[inlined\] (?P<inline_symbol>.*)){0,1} at (?P<file>[^:]+):(?P<line_no>[0-9]+)(?P<column>(:[0-9]+)?), address = (?P<address>0x[0-9a-fA-F]+)$",537 r"^Breakpoint (?P<bpno>[0-9]+): where = (?P<module>.*)`(?P<symbol>.*)( \+ (?P<offset>[0-9]+)){0,1}, address = (?P<address>0x[0-9a-fA-F]+)$",538 ]539 match_object = test.match(command, patterns)540 break_results = match_object.groupdict()541 542 # We always insert the breakpoint number, setting it to -1 if we couldn't find it543 # Also, make sure it gets stored as an integer.544 if not "bpno" in break_results:545 break_results["bpno"] = -1546 else:547 break_results["bpno"] = int(break_results["bpno"])548 549 # We always insert the number of locations550 # If ONE location is set for the breakpoint, then the output doesn't mention locations, but it has to be 1...551 # We also make sure it is an integer.552 553 if not "num_locations" in break_results:554 num_locations = 1555 else:556 num_locations = break_results["num_locations"]557 if num_locations == "no":558 num_locations = 0559 else:560 num_locations = int(break_results["num_locations"])561 562 break_results["num_locations"] = num_locations563 564 if "line_no" in break_results:565 break_results["line_no"] = int(break_results["line_no"])566 567 return break_results568 569 570def get_bpno_from_match(break_results):571 return int(break_results["bpno"])572 573 574def check_breakpoint_result(575 test,576 break_results,577 file_name=None,578 line_number=-1,579 column_number=0,580 symbol_name=None,581 symbol_match_exact=True,582 module_name=None,583 offset=-1,584 num_locations=-1,585):586 out_num_locations = break_results["num_locations"]587 588 if num_locations == -1:589 test.assertTrue(590 out_num_locations > 0, "Expecting one or more locations, got none."591 )592 elif num_locations != -2:593 test.assertTrue(594 num_locations == out_num_locations,595 "Expecting %d locations, got %d." % (num_locations, out_num_locations),596 )597 598 if file_name:599 out_file_name = ""600 if "file" in break_results:601 out_file_name = break_results["file"]602 test.assertTrue(603 file_name.endswith(out_file_name),604 "Breakpoint file name '%s' doesn't match resultant name '%s'."605 % (file_name, out_file_name),606 )607 608 if line_number != -1:609 out_line_number = -1610 if "line_no" in break_results:611 out_line_number = break_results["line_no"]612 613 test.assertTrue(614 line_number == out_line_number,615 "Breakpoint line number %s doesn't match resultant line %s."616 % (line_number, out_line_number),617 )618 619 if column_number != 0:620 out_column_number = 0621 if "column" in break_results:622 out_column_number = break_results["column"]623 624 test.assertTrue(625 column_number == out_column_number,626 "Breakpoint column number %s doesn't match resultant column %s."627 % (column_number, out_column_number),628 )629 630 if symbol_name:631 out_symbol_name = ""632 # Look first for the inlined symbol name, otherwise use the symbol633 # name:634 if "inline_symbol" in break_results and break_results["inline_symbol"]:635 out_symbol_name = break_results["inline_symbol"]636 elif "symbol" in break_results:637 out_symbol_name = break_results["symbol"]638 639 if symbol_match_exact:640 test.assertTrue(641 symbol_name == out_symbol_name,642 "Symbol name '%s' doesn't match resultant symbol '%s'."643 % (symbol_name, out_symbol_name),644 )645 else:646 test.assertTrue(647 out_symbol_name.find(symbol_name) != -1,648 "Symbol name '%s' isn't in resultant symbol '%s'."649 % (symbol_name, out_symbol_name),650 )651 652 if module_name:653 out_module_name = None654 if "module" in break_results:655 out_module_name = break_results["module"]656 657 test.assertTrue(658 module_name.find(out_module_name) != -1,659 "Symbol module name '%s' isn't in expected module name '%s'."660 % (out_module_name, module_name),661 )662 663 664def check_breakpoint(665 test,666 bpno,667 expected_locations=None,668 expected_resolved_count=None,669 expected_hit_count=None,670 location_id=None,671 expected_location_resolved=True,672 expected_location_hit_count=None,673):674 """675 Test breakpoint or breakpoint location.676 Breakpoint resolved count is always checked. If not specified the assumption is that all locations677 should be resolved.678 To test a breakpoint location, breakpoint number (bpno) and location_id must be set. In this case679 the resolved count for a breakpoint is not tested by default. The location is expected to be resolved,680 unless expected_location_resolved is set to False.681 test - test context682 bpno - breakpoint number to test683 expected_locations - expected number of locations for this breakpoint. If 'None' this parameter is not tested.684 expected_resolved_count - expected resolved locations number for the breakpoint. If 'None' - all locations should be resolved.685 expected_hit_count - expected hit count for this breakpoint. If 'None' this parameter is not tested.686 location_id - If not 'None' sets the location ID for the breakpoint to test.687 expected_location_resolved - Extected resolved status for the location_id (True/False). Default - True.688 expected_location_hit_count - Expected hit count for the breakpoint at location_id. Must be set if the location_id parameter is set.689 """690 691 if isinstance(test.target, lldb.SBTarget):692 target = test.target693 else:694 target = test.target()695 bkpt = target.FindBreakpointByID(bpno)696 697 test.assertTrue(bkpt.IsValid(), "Breakpoint is not valid.")698 699 if expected_locations is not None:700 test.assertEqual(expected_locations, bkpt.GetNumLocations())701 702 if expected_resolved_count is not None:703 test.assertEqual(expected_resolved_count, bkpt.GetNumResolvedLocations())704 else:705 expected_resolved_count = bkpt.GetNumLocations()706 if location_id is None:707 test.assertEqual(expected_resolved_count, bkpt.GetNumResolvedLocations())708 709 if expected_hit_count is not None:710 test.assertEqual(expected_hit_count, bkpt.GetHitCount())711 712 if location_id is not None:713 loc_bkpt = bkpt.FindLocationByID(location_id)714 test.assertTrue(loc_bkpt.IsValid(), "Breakpoint location is not valid.")715 test.assertEqual(loc_bkpt.IsResolved(), expected_location_resolved)716 if expected_location_hit_count is not None:717 test.assertEqual(expected_location_hit_count, loc_bkpt.GetHitCount())718 719 720# ==================================================721# Utility functions related to Threads and Processes722# ==================================================723 724 725def get_stopped_threads(process, reason):726 """Returns the thread(s) with the specified stop reason in a list.727 728 The list can be empty if no such thread exists.729 """730 threads = []731 for t in process:732 if t.GetStopReason() == reason:733 threads.append(t)734 return threads735 736 737def get_stopped_thread(process, reason):738 """A convenience function which returns the first thread with the given stop739 reason or None.740 741 Example usages:742 743 1. Get the stopped thread due to a breakpoint condition744 745 ...746 from lldbutil import get_stopped_thread747 thread = get_stopped_thread(process, lldb.eStopReasonPlanComplete)748 self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint condition")749 ...750 751 2. Get the thread stopped due to a breakpoint752 753 ...754 from lldbutil import get_stopped_thread755 thread = get_stopped_thread(process, lldb.eStopReasonBreakpoint)756 self.assertTrue(thread.IsValid(), "There should be a thread stopped due to breakpoint")757 ...758 759 """760 threads = get_stopped_threads(process, reason)761 if len(threads) == 0:762 return None763 return threads[0]764 765 766def get_threads_stopped_at_breakpoint_id(process, bpid):767 """For a stopped process returns the thread stopped at the breakpoint passed in bkpt"""768 stopped_threads = []769 threads = []770 771 stopped_threads = get_stopped_threads(process, lldb.eStopReasonBreakpoint)772 773 if len(stopped_threads) == 0:774 return threads775 776 for thread in stopped_threads:777 # Make sure we've hit our breakpoint.778 # From the docs of GetStopReasonDataAtIndex: "Breakpoint stop reasons779 # will have data that consists of pairs of breakpoint IDs followed by780 # the breakpoint location IDs".781 # Iterate over all such pairs looking for `bpid`.782 break_ids = [783 thread.GetStopReasonDataAtIndex(idx)784 for idx in range(0, thread.GetStopReasonDataCount(), 2)785 ]786 if bpid in break_ids:787 threads.append(thread)788 789 return threads790 791 792def get_threads_stopped_at_breakpoint(process, bkpt):793 return get_threads_stopped_at_breakpoint_id(process, bkpt.GetID())794 795 796def get_one_thread_stopped_at_breakpoint_id(process, bpid, require_exactly_one=True):797 threads = get_threads_stopped_at_breakpoint_id(process, bpid)798 if len(threads) == 0:799 return None800 if require_exactly_one and len(threads) != 1:801 return None802 803 return threads[0]804 805 806def get_one_thread_stopped_at_breakpoint(process, bkpt, require_exactly_one=True):807 return get_one_thread_stopped_at_breakpoint_id(808 process, bkpt.GetID(), require_exactly_one809 )810 811 812def is_thread_crashed(test, thread):813 """In the test suite we dereference a null pointer to simulate a crash. The way this is814 reported depends on the platform."""815 if test.platformIsDarwin():816 return (817 thread.GetStopReason() == lldb.eStopReasonException818 and "EXC_BAD_ACCESS" in thread.GetStopDescription(100)819 )820 elif test.getPlatform() in ["linux", "freebsd"]:821 return (822 thread.GetStopReason() == lldb.eStopReasonSignal823 and thread.GetStopReasonDataAtIndex(0)824 == thread.GetProcess().GetUnixSignals().GetSignalNumberFromName("SIGSEGV")825 )826 elif test.getPlatform() == "windows":827 return "Exception 0xc0000005" in thread.GetStopDescription(200)828 else:829 return "invalid address" in thread.GetStopDescription(100)830 831 832def get_crashed_threads(test, process):833 threads = []834 if process.GetState() != lldb.eStateStopped:835 return threads836 for thread in process:837 if is_thread_crashed(test, thread):838 threads.append(thread)839 return threads840 841 842# Helper functions for run_to_{source,name}_breakpoint:843 844 845def run_to_breakpoint_make_target(test, exe_name="a.out", in_cwd=True):846 exe = test.getBuildArtifact(exe_name) if in_cwd else exe_name847 848 # Create the target849 target = test.dbg.CreateTarget(exe)850 test.assertTrue(target, "Target: %s is not valid." % (exe_name))851 852 # Set environment variables for the inferior.853 if lldbtest_config.inferior_env:854 test.runCmd(855 "settings set target.env-vars {}".format(lldbtest_config.inferior_env)856 )857 858 return target859 860 861def run_to_breakpoint_do_run(862 test, target, bkpt, launch_info=None, only_one_thread=True, extra_images=None863):864 # Launch the process, and do not stop at the entry point.865 if not launch_info:866 launch_info = target.GetLaunchInfo()867 launch_info.SetWorkingDirectory(test.get_process_working_directory())868 869 if extra_images:870 environ = test.registerSharedLibrariesWithTarget(target, extra_images)871 launch_info.SetEnvironmentEntries(environ, True)872 873 error = lldb.SBError()874 process = target.Launch(launch_info, error)875 876 # Unfortunate workaround for the iPhone simulator.877 retry = SIMULATOR_RETRY878 while (879 retry880 and error.Fail()881 and error.GetCString()882 and "Unable to boot the Simulator" in error.GetCString()883 ):884 retry -= 1885 print("** Simulator is unresponsive. Retrying %d more time(s)" % retry)886 import time887 888 time.sleep(60)889 error = lldb.SBError()890 process = target.Launch(launch_info, error)891 892 test.assertTrue(893 process,894 "Could not create a valid process for %s: %s"895 % (target.GetExecutable().GetFilename(), error.GetCString()),896 )897 test.assertFalse(error.Fail(), "Process launch failed: %s" % (error.GetCString()))898 899 def processStateInfo(process):900 info = "state: {}".format(state_type_to_str(process.state))901 if process.state == lldb.eStateExited:902 info += ", exit code: {}".format(process.GetExitStatus())903 if process.exit_description:904 info += ", exit description: '{}'".format(process.exit_description)905 stdout = process.GetSTDOUT(999)906 if stdout:907 info += ", stdout: '{}'".format(stdout)908 stderr = process.GetSTDERR(999)909 if stderr:910 info += ", stderr: '{}'".format(stderr)911 return info912 913 if process.state != lldb.eStateStopped:914 test.fail(915 "Test process is not stopped at breakpoint: {}".format(916 processStateInfo(process)917 )918 )919 920 # Frame #0 should be at our breakpoint.921 threads = get_threads_stopped_at_breakpoint(process, bkpt)922 923 num_threads = len(threads)924 if only_one_thread:925 test.assertEqual(926 num_threads,927 1,928 "Expected 1 thread to stop at breakpoint, %d did." % (num_threads),929 )930 else:931 test.assertGreater(num_threads, 0, "No threads stopped at breakpoint")932 933 thread = threads[0]934 return (target, process, thread, bkpt)935 936 937def run_to_name_breakpoint(938 test,939 bkpt_name,940 launch_info=None,941 exe_name="a.out",942 bkpt_module=None,943 in_cwd=True,944 only_one_thread=True,945 extra_images=None,946):947 """Start up a target, using exe_name as the executable, and run it to948 a breakpoint set by name on bkpt_name restricted to bkpt_module.949 950 If you want to pass in launch arguments or environment951 variables, you can optionally pass in an SBLaunchInfo. If you952 do that, remember to set the working directory as well.953 954 If your executable isn't called a.out, you can pass that in.955 And if your executable isn't in the CWD, pass in the absolute956 path to the executable in exe_name, and set in_cwd to False.957 958 If you need to restrict the breakpoint to a particular module,959 pass the module name (a string not a FileSpec) in bkpt_module. If960 nothing is passed in setting will be unrestricted.961 962 If the target isn't valid, the breakpoint isn't found, or hit, the963 function will cause a testsuite failure.964 965 If successful it returns a tuple with the target process and966 thread that hit the breakpoint, and the breakpoint that we set967 for you.968 969 If only_one_thread is true, we require that there be only one970 thread stopped at the breakpoint. Otherwise we only require one971 or more threads stop there. If there are more than one, we return972 the first thread that stopped.973 """974 975 target = run_to_breakpoint_make_target(test, exe_name, in_cwd)976 977 breakpoint = target.BreakpointCreateByName(bkpt_name, bkpt_module)978 979 test.assertTrue(980 breakpoint.GetNumLocations() > 0,981 "No locations found for name breakpoint: '%s'." % (bkpt_name),982 )983 return run_to_breakpoint_do_run(984 test, target, breakpoint, launch_info, only_one_thread, extra_images985 )986 987 988def run_to_source_breakpoint(989 test,990 bkpt_pattern,991 source_spec,992 launch_info=None,993 exe_name="a.out",994 bkpt_module=None,995 in_cwd=True,996 only_one_thread=True,997 extra_images=None,998 has_locations_before_run=True,999):1000 """Start up a target, using exe_name as the executable, and run it to1001 a breakpoint set by source regex bkpt_pattern.1002 1003 The rest of the behavior is the same as run_to_name_breakpoint.1004 """1005 1006 target = run_to_breakpoint_make_target(test, exe_name, in_cwd)1007 # Set the breakpoints1008 breakpoint = target.BreakpointCreateBySourceRegex(1009 bkpt_pattern, source_spec, bkpt_module1010 )1011 if has_locations_before_run:1012 test.assertTrue(1013 breakpoint.GetNumLocations() > 0,1014 'No locations found for source breakpoint: "%s", file: "%s", dir: "%s"'1015 % (bkpt_pattern, source_spec.GetFilename(), source_spec.GetDirectory()),1016 )1017 return run_to_breakpoint_do_run(1018 test, target, breakpoint, launch_info, only_one_thread, extra_images1019 )1020 1021 1022def run_to_line_breakpoint(1023 test,1024 source_spec,1025 line_number,1026 column=0,1027 launch_info=None,1028 exe_name="a.out",1029 bkpt_module=None,1030 in_cwd=True,1031 only_one_thread=True,1032 extra_images=None,1033):1034 """Start up a target, using exe_name as the executable, and run it to1035 a breakpoint set by (source_spec, line_number(, column)).1036 1037 The rest of the behavior is the same as run_to_name_breakpoint.1038 """1039 1040 target = run_to_breakpoint_make_target(test, exe_name, in_cwd)1041 # Set the breakpoints1042 breakpoint = target.BreakpointCreateByLocation(1043 source_spec, line_number, column, 0, lldb.SBFileSpecList()1044 )1045 test.assertTrue(1046 breakpoint.GetNumLocations() > 0,1047 'No locations found for line breakpoint: "%s:%d(:%d)", dir: "%s"'1048 % (source_spec.GetFilename(), line_number, column, source_spec.GetDirectory()),1049 )1050 return run_to_breakpoint_do_run(1051 test, target, breakpoint, launch_info, only_one_thread, extra_images1052 )1053 1054 1055def continue_to_breakpoint(process, bkpt):1056 """Continues the process, if it stops, returns the threads stopped at bkpt; otherwise, returns None"""1057 process.Continue()1058 if process.GetState() != lldb.eStateStopped:1059 return None1060 else:1061 return get_threads_stopped_at_breakpoint(process, bkpt)1062 1063 1064def continue_to_source_breakpoint(test, process, bkpt_pattern, source_spec):1065 """1066 Sets a breakpoint set by source regex bkpt_pattern, continues the process, and deletes the breakpoint again.1067 Otherwise the same as `continue_to_breakpoint`1068 """1069 breakpoint = process.target.BreakpointCreateBySourceRegex(1070 bkpt_pattern, source_spec, None1071 )1072 test.assertTrue(1073 breakpoint.GetNumLocations() > 0,1074 'No locations found for source breakpoint: "%s", file: "%s", dir: "%s"'1075 % (bkpt_pattern, source_spec.GetFilename(), source_spec.GetDirectory()),1076 )1077 stopped_threads = continue_to_breakpoint(process, breakpoint)1078 process.target.BreakpointDelete(breakpoint.GetID())1079 return stopped_threads1080 1081 1082def get_caller_symbol(thread):1083 """1084 Returns the symbol name for the call site of the leaf function.1085 """1086 depth = thread.GetNumFrames()1087 if depth <= 1:1088 return None1089 caller = thread.GetFrameAtIndex(1).GetSymbol()1090 if caller:1091 return caller.GetName()1092 else:1093 return None1094 1095 1096def get_function_names(thread):1097 """1098 Returns a sequence of function names from the stack frames of this thread.1099 """1100 1101 def GetFuncName(i):1102 return thread.GetFrameAtIndex(i).GetFunctionName()1103 1104 return list(map(GetFuncName, list(range(thread.GetNumFrames()))))1105 1106 1107def get_symbol_names(thread):1108 """1109 Returns a sequence of symbols for this thread.1110 """1111 1112 def GetSymbol(i):1113 return thread.GetFrameAtIndex(i).GetSymbol().GetName()1114 1115 return list(map(GetSymbol, list(range(thread.GetNumFrames()))))1116 1117 1118def get_pc_addresses(thread):1119 """1120 Returns a sequence of pc addresses for this thread.1121 """1122 1123 def GetPCAddress(i):1124 return thread.GetFrameAtIndex(i).GetPCAddress()1125 1126 return list(map(GetPCAddress, list(range(thread.GetNumFrames()))))1127 1128 1129def get_filenames(thread):1130 """1131 Returns a sequence of file names from the stack frames of this thread.1132 """1133 1134 def GetFilename(i):1135 return thread.GetFrameAtIndex(i).GetLineEntry().GetFileSpec().GetFilename()1136 1137 return list(map(GetFilename, list(range(thread.GetNumFrames()))))1138 1139 1140def get_line_numbers(thread):1141 """1142 Returns a sequence of line numbers from the stack frames of this thread.1143 """1144 1145 def GetLineNumber(i):1146 return thread.GetFrameAtIndex(i).GetLineEntry().GetLine()1147 1148 return list(map(GetLineNumber, list(range(thread.GetNumFrames()))))1149 1150 1151def get_module_names(thread):1152 """1153 Returns a sequence of module names from the stack frames of this thread.1154 """1155 1156 def GetModuleName(i):1157 return thread.GetFrameAtIndex(i).GetModule().GetFileSpec().GetFilename()1158 1159 return list(map(GetModuleName, list(range(thread.GetNumFrames()))))1160 1161 1162def print_stacktrace(thread, string_buffer=False):1163 """Prints a simple stack trace of this thread."""1164 1165 output = io.StringIO() if string_buffer else sys.stdout1166 target = thread.GetProcess().GetTarget()1167 1168 depth = thread.GetNumFrames()1169 1170 mods = get_module_names(thread)1171 funcs = get_function_names(thread)1172 symbols = get_symbol_names(thread)1173 files = get_filenames(thread)1174 lines = get_line_numbers(thread)1175 addrs = get_pc_addresses(thread)1176 1177 if thread.GetStopReason() != lldb.eStopReasonInvalid:1178 desc = "stop reason=" + stop_reason_to_str(thread.GetStopReason())1179 else:1180 desc = ""1181 print(1182 "Stack trace for thread id={0:#x} name={1} queue={2} ".format(1183 thread.GetThreadID(), thread.GetName(), thread.GetQueueName()1184 )1185 + desc,1186 file=output,1187 )1188 1189 for i in range(depth):1190 frame = thread.GetFrameAtIndex(i)1191 function = frame.GetFunction()1192 1193 load_addr = addrs[i].GetLoadAddress(target)1194 if not function:1195 file_addr = addrs[i].GetFileAddress()1196 start_addr = frame.GetSymbol().GetStartAddress().GetFileAddress()1197 symbol_offset = file_addr - start_addr1198 print(1199 " frame #{num}: {addr:#016x} {mod}`{symbol} + {offset}".format(1200 num=i,1201 addr=load_addr,1202 mod=mods[i],1203 symbol=symbols[i],1204 offset=symbol_offset,1205 ),1206 file=output,1207 )1208 else:1209 print(1210 " frame #{num}: {addr:#016x} {mod}`{func} at {file}:{line} {args}".format(1211 num=i,1212 addr=load_addr,1213 mod=mods[i],1214 func="%s [inlined]" % funcs[i] if frame.IsInlined() else funcs[i],1215 file=files[i],1216 line=lines[i],1217 args=get_args_as_string(frame, showFuncName=False)1218 if not frame.IsInlined()1219 else "()",1220 ),1221 file=output,1222 )1223 1224 if string_buffer:1225 return output.getvalue()1226 1227 1228def print_stacktraces(process, string_buffer=False):1229 """Prints the stack traces of all the threads."""1230 1231 output = io.StringIO() if string_buffer else sys.stdout1232 1233 print("Stack traces for " + str(process), file=output)1234 1235 for thread in process:1236 print(print_stacktrace(thread, string_buffer=True), file=output)1237 1238 if string_buffer:1239 return output.getvalue()1240 1241 1242def expect_state_changes(test, listener, process, states, timeout=30):1243 """Listens for state changed events on the listener and makes sure they match what we1244 expect. Stop-and-restart events (where GetRestartedFromEvent() returns true) are ignored.1245 """1246 1247 for expected_state in states:1248 1249 def get_next_event():1250 event = lldb.SBEvent()1251 if not listener.WaitForEventForBroadcasterWithType(1252 timeout,1253 process.GetBroadcaster(),1254 lldb.SBProcess.eBroadcastBitStateChanged,1255 event,1256 ):1257 test.fail(1258 "Timed out while waiting for a transition to state %s"1259 % lldb.SBDebugger.StateAsCString(expected_state)1260 )1261 return event1262 1263 event = get_next_event()1264 while lldb.SBProcess.GetStateFromEvent(1265 event1266 ) == lldb.eStateStopped and lldb.SBProcess.GetRestartedFromEvent(event):1267 # Ignore restarted event and the subsequent running event.1268 event = get_next_event()1269 test.assertEqual(1270 lldb.SBProcess.GetStateFromEvent(event),1271 lldb.eStateRunning,1272 "Restarted event followed by a running event",1273 )1274 event = get_next_event()1275 1276 test.assertEqual(lldb.SBProcess.GetStateFromEvent(event), expected_state)1277 1278 1279def start_listening_from(broadcaster, event_mask):1280 """Creates a listener for a specific event mask and add it to the source broadcaster."""1281 1282 listener = lldb.SBListener("lldb.test.listener")1283 broadcaster.AddListener(listener, event_mask)1284 return listener1285 1286 1287def fetch_next_event(test, listener, broadcaster, match_class=False, timeout=10):1288 """Fetch one event from the listener and return it if it matches the provided broadcaster.1289 If `match_class` is set to True, this will match an event with an entire broadcaster class.1290 Fails otherwise."""1291 1292 event = lldb.SBEvent()1293 1294 if listener.WaitForEvent(timeout, event):1295 if match_class:1296 if event.GetBroadcasterClass() == broadcaster:1297 return event1298 else:1299 if event.BroadcasterMatchesRef(broadcaster):1300 return event1301 1302 stream = lldb.SBStream()1303 event.GetDescription(stream)1304 test.fail(1305 "received event '%s' from unexpected broadcaster '%s'."1306 % (stream.GetData(), event.GetBroadcaster().GetName())1307 )1308 1309 test.fail("couldn't fetch an event before reaching the timeout.")1310 1311 1312# ===================================1313# Utility functions related to Frames1314# ===================================1315 1316 1317def get_parent_frame(frame):1318 """1319 Returns the parent frame of the input frame object; None if not available.1320 """1321 thread = frame.GetThread()1322 parent_found = False1323 for f in thread:1324 if parent_found:1325 return f1326 if f.GetFrameID() == frame.GetFrameID():1327 parent_found = True1328 1329 # If we reach here, no parent has been found, return None.1330 return None1331 1332 1333def get_args_as_string(frame, showFuncName=True):1334 """1335 Returns the args of the input frame object as a string.1336 """1337 # arguments => True1338 # locals => False1339 # statics => False1340 # in_scope_only => True1341 vars = frame.GetVariables(True, False, False, True) # type of SBValueList1342 args = [] # list of strings1343 for var in vars:1344 args.append("(%s)%s=%s" % (var.GetTypeName(), var.GetName(), var.GetValue()))1345 if frame.GetFunction():1346 name = frame.GetFunction().GetName()1347 elif frame.GetSymbol():1348 name = frame.GetSymbol().GetName()1349 else:1350 name = ""1351 if showFuncName:1352 return "%s(%s)" % (name, ", ".join(args))1353 else:1354 return "(%s)" % (", ".join(args))1355 1356 1357def get_registers(frame, kind):1358 """Returns the registers given the frame and the kind of registers desired.1359 1360 Returns None if there's no such kind.1361 """1362 registerSet = frame.GetRegisters() # Return type of SBValueList.1363 for value in registerSet:1364 if kind.lower() in value.GetName().lower():1365 return value1366 1367 return None1368 1369 1370def get_GPRs(frame):1371 """Returns the general purpose registers of the frame as an SBValue.1372 1373 The returned SBValue object is iterable. An example:1374 ...1375 from lldbutil import get_GPRs1376 regs = get_GPRs(frame)1377 for reg in regs:1378 print("%s => %s" % (reg.GetName(), reg.GetValue()))1379 ...1380 """1381 return get_registers(frame, "general purpose")1382 1383 1384def get_FPRs(frame):1385 """Returns the floating point registers of the frame as an SBValue.1386 1387 The returned SBValue object is iterable. An example:1388 ...1389 from lldbutil import get_FPRs1390 regs = get_FPRs(frame)1391 for reg in regs:1392 print("%s => %s" % (reg.GetName(), reg.GetValue()))1393 ...1394 """1395 return get_registers(frame, "floating point")1396 1397 1398def get_ESRs(frame):1399 """Returns the exception state registers of the frame as an SBValue.1400 1401 The returned SBValue object is iterable. An example:1402 ...1403 from lldbutil import get_ESRs1404 regs = get_ESRs(frame)1405 for reg in regs:1406 print("%s => %s" % (reg.GetName(), reg.GetValue()))1407 ...1408 """1409 return get_registers(frame, "exception state")1410 1411 1412# ======================================1413# Utility classes/functions for SBValues1414# ======================================1415 1416 1417class BasicFormatter(object):1418 """The basic formatter inspects the value object and prints the value."""1419 1420 def format(self, value, buffer=None, indent=0):1421 if not buffer:1422 output = io.StringIO()1423 else:1424 output = buffer1425 # If there is a summary, it suffices.1426 val = value.GetSummary()1427 # Otherwise, get the value.1428 if val is None:1429 val = value.GetValue()1430 if val is None and value.GetNumChildren() > 0:1431 val = "%s (location)" % value.GetLocation()1432 print(1433 "{indentation}({type}) {name} = {value}".format(1434 indentation=" " * indent,1435 type=value.GetTypeName(),1436 name=value.GetName(),1437 value=val,1438 ),1439 file=output,1440 )1441 return output.getvalue()1442 1443 1444class ChildVisitingFormatter(BasicFormatter):1445 """The child visiting formatter prints the value and its immediate children.1446 1447 The constructor takes a keyword arg: indent_child, which defaults to 2.1448 """1449 1450 def __init__(self, indent_child=2):1451 """Default indentation of 2 SPC's for the children."""1452 self.cindent = indent_child1453 1454 def format(self, value, buffer=None):1455 if not buffer:1456 output = io.StringIO()1457 else:1458 output = buffer1459 1460 BasicFormatter.format(self, value, buffer=output)1461 for child in value:1462 BasicFormatter.format(self, child, buffer=output, indent=self.cindent)1463 1464 return output.getvalue()1465 1466 1467class RecursiveDescentFormatter(BasicFormatter):1468 """The recursive descent formatter prints the value and the descendents.1469 1470 The constructor takes two keyword args: indent_level, which defaults to 0,1471 and indent_child, which defaults to 2. The current indentation level is1472 determined by indent_level, while the immediate children has an additional1473 indentation by inden_child.1474 """1475 1476 def __init__(self, indent_level=0, indent_child=2):1477 self.lindent = indent_level1478 self.cindent = indent_child1479 1480 def format(self, value, buffer=None):1481 if not buffer:1482 output = io.StringIO()1483 else:1484 output = buffer1485 BasicFormatter.format(self, value, buffer=output, indent=self.lindent)1486 new_indent = self.lindent + self.cindent1487 for child in value:1488 if child.GetSummary() is not None:1489 BasicFormatter.format(self, child, buffer=output, indent=new_indent)1490 else:1491 if child.GetNumChildren() > 0:1492 rdf = RecursiveDescentFormatter(indent_level=new_indent)1493 rdf.format(child, buffer=output)1494 else:1495 BasicFormatter.format(self, child, buffer=output, indent=new_indent)1496 1497 return output.getvalue()1498 1499 1500# ===========================================================1501# Utility functions for path manipulation on remote platforms1502# ===========================================================1503 1504 1505def join_remote_paths(*paths):1506 # TODO: update with actual platform name for remote windows once it exists1507 if lldb.remote_platform.GetName() == "remote-windows":1508 return os.path.join(*paths).replace(os.path.sep, "\\")1509 return os.path.join(*paths).replace(os.path.sep, "/")1510 1511 1512def append_to_process_working_directory(test, *paths):1513 remote = lldb.remote_platform1514 if remote:1515 return join_remote_paths(remote.GetWorkingDirectory(), *paths)1516 return os.path.join(test.getBuildDir(), *paths)1517 1518 1519# ==================================================1520# Utility functions to get the correct signal number1521# ==================================================1522 1523import signal1524 1525 1526def get_signal_number(signal_name):1527 platform = lldb.remote_platform1528 if platform and platform.IsValid():1529 signals = platform.GetUnixSignals()1530 if signals.IsValid():1531 signal_number = signals.GetSignalNumberFromName(signal_name)1532 if signal_number > 0:1533 return signal_number1534 # No remote platform; fall back to using local python signals.1535 return getattr(signal, signal_name)1536 1537 1538def get_actions_for_signal(1539 testcase, signal_name, from_target=False, expected_absent=False1540):1541 """Returns a triple of (pass, stop, notify)"""1542 return_obj = lldb.SBCommandReturnObject()1543 command = "process handle {0}".format(signal_name)1544 if from_target:1545 command += " -t"1546 testcase.dbg.GetCommandInterpreter().HandleCommand(command, return_obj)1547 match = re.match(1548 "NAME *PASS *STOP *NOTIFY.*(false|true|not set) *(false|true|not set) *(false|true|not set)",1549 return_obj.GetOutput(),1550 re.IGNORECASE | re.DOTALL,1551 )1552 if match and expected_absent:1553 testcase.fail('Signal "{0}" was supposed to be absent'.format(signal_name))1554 if not match:1555 if expected_absent:1556 return (None, None, None)1557 testcase.fail("Unable to retrieve default signal disposition.")1558 return (match.group(1), match.group(2), match.group(3))1559 1560 1561def set_actions_for_signal(1562 testcase, signal_name, pass_action, stop_action, notify_action, expect_success=True1563):1564 return_obj = lldb.SBCommandReturnObject()1565 command = "process handle {0}".format(signal_name)1566 if pass_action is not None:1567 command += " -p {0}".format(pass_action)1568 if stop_action is not None:1569 command += " -s {0}".format(stop_action)1570 if notify_action is not None:1571 command += " -n {0}".format(notify_action)1572 1573 testcase.dbg.GetCommandInterpreter().HandleCommand(command, return_obj)1574 testcase.assertEqual(1575 expect_success,1576 return_obj.Succeeded(),1577 "Setting signal handling for {0} worked as expected".format(signal_name),1578 )1579 1580 1581def skip_if_callable(test, mycallable, reason):1582 if callable(mycallable):1583 if mycallable(test):1584 test.skipTest(reason)1585 return True1586 return False1587 1588 1589def skip_if_library_missing(test, target, library):1590 def find_library(target, library):1591 for module in target.modules:1592 filename = module.file.GetFilename()1593 if isinstance(library, str):1594 if library == filename:1595 return False1596 elif hasattr(library, "match"):1597 if library.match(filename):1598 return False1599 return True1600 1601 def find_library_callable(test):1602 return find_library(target, library)1603 1604 return skip_if_callable(1605 test,1606 find_library_callable,1607 "could not find library matching '%s' in target %s" % (library, target),1608 )1609 1610 1611def install_to_target(test, path):1612 if lldb.remote_platform:1613 filename = os.path.basename(path)1614 remote_path = append_to_process_working_directory(test, filename)1615 err = lldb.remote_platform.Install(1616 lldb.SBFileSpec(path, True), lldb.SBFileSpec(remote_path, False)1617 )1618 if err.Fail():1619 raise Exception(1620 "remote_platform.Install('%s', '%s') failed: %s"1621 % (path, remote_path, err)1622 )1623 path = remote_path1624 return path1625 1626 1627def read_file_on_target(test, remote):1628 if lldb.remote_platform:1629 local = test.getBuildArtifact("file_from_target")1630 error = lldb.remote_platform.Get(1631 lldb.SBFileSpec(remote, False), lldb.SBFileSpec(local, True)1632 )1633 test.assertTrue(1634 error.Success(), "Reading file {0} failed: {1}".format(remote, error)1635 )1636 else:1637 local = remote1638 with open(local, "r") as f:1639 return f.read()1640 1641 1642def read_file_from_process_wd(test, name):1643 path = append_to_process_working_directory(test, name)1644 return read_file_on_target(test, path)1645 1646 1647def wait_for_file_on_target(testcase, file_path, max_attempts=6):1648 for i in range(max_attempts):1649 err, retcode, msg = testcase.run_platform_command("ls %s" % file_path)1650 if err.Success() and retcode == 0:1651 break1652 if i < max_attempts:1653 # Exponential backoff!1654 import time1655 1656 time.sleep(pow(2, i) * 0.25)1657 else:1658 testcase.fail(1659 "File %s not found even after %d attempts." % (file_path, max_attempts)1660 )1661 1662 return read_file_on_target(testcase, file_path)1663 1664 1665def packetlog_get_process_info(log):1666 """parse a gdb-remote packet log file and extract the response to qProcessInfo"""1667 process_info = dict()1668 with open(log, "r") as logfile:1669 process_info_ostype = None1670 expect_process_info_response = False1671 for line in logfile:1672 if expect_process_info_response:1673 for pair in line.split(";"):1674 keyval = pair.split(":")1675 if len(keyval) == 2:1676 process_info[keyval[0]] = keyval[1]1677 break1678 if "send packet: $qProcessInfo#" in line:1679 expect_process_info_response = True1680 return process_info1681 1682 1683def packetlog_get_dylib_info(log):1684 """parse a gdb-remote packet log file and extract the *last* complete1685 (=> fetch_all_solibs=true) response to jGetLoadedDynamicLibrariesInfos"""1686 import json1687 1688 dylib_info = None1689 with open(log, "r") as logfile:1690 dylib_info = None1691 expect_dylib_info_response = False1692 for line in logfile:1693 if expect_dylib_info_response:1694 while line[0] != "$":1695 line = line[1:]1696 line = line[1:]1697 # Unescape '}'.1698 dylib_info = json.loads(line.replace("}]", "}")[:-4])1699 expect_dylib_info_response = False1700 if (1701 'send packet: $jGetLoadedDynamicLibrariesInfos:{"fetch_all_solibs":true}'1702 in line1703 ):1704 expect_dylib_info_response = True1705 1706 return dylib_info1707 1708 1709# ========================1710# Utilities for simulators1711# ========================1712 1713 1714def get_latest_apple_simulator(platform_name, log=None):1715 # Run simctl to list all simulators1716 cmd = ["xcrun", "simctl", "list", "-j", "devices"]1717 cmd_str = " ".join(cmd)1718 if log:1719 log(cmd_str)1720 sim_devices_str = subprocess.check_output(cmd).decode("utf-8")1721 sim_devices = json.loads(sim_devices_str)["devices"]1722 1723 # Find an available simulator for the requested platform1724 device_uuid = None1725 device_runtime = None1726 for simulator in sim_devices:1727 if isinstance(simulator, dict):1728 runtime = simulator["name"]1729 devices = simulator["devices"]1730 else:1731 runtime = simulator1732 devices = sim_devices[simulator]1733 if not platform_name in runtime.lower():1734 continue1735 for device in devices:1736 if "availability" in device and device["availability"] != "(available)":1737 continue1738 if "isAvailable" in device and not device["isAvailable"]:1739 continue1740 if device_runtime and runtime < device_runtime:1741 continue1742 device_uuid = device["udid"]1743 device_runtime = runtime1744 # Stop searching in this runtime1745 break1746 1747 return device_uuid1748 1749 1750def launch_exe_in_apple_simulator(1751 device_uuid,1752 exe_path,1753 exe_args=[],1754 stderr_lines_to_read=0,1755 stderr_patterns=[],1756 log=None,1757):1758 exe_path = os.path.realpath(exe_path)1759 cmd = [1760 "xcrun",1761 "simctl",1762 "spawn",1763 "-s",1764 device_uuid,1765 exe_path,1766 ] + exe_args1767 if log:1768 log(" ".join(cmd))1769 sim_launcher = subprocess.Popen(cmd, stderr=subprocess.PIPE)1770 1771 # Read stderr to try to find matches.1772 # Each pattern will return the value of group[1] of the first match in the stderr.1773 # Will read at most stderr_lines_to_read lines.1774 # Will early terminate when all matches have been found.1775 total_patterns = len(stderr_patterns)1776 matches_found = 01777 matched_strings = [None] * total_patterns1778 for _ in range(0, stderr_lines_to_read):1779 stderr = sim_launcher.stderr.readline().decode("utf-8")1780 if not stderr:1781 continue1782 for i, pattern in enumerate(stderr_patterns):1783 if matched_strings[i] is not None:1784 continue1785 match = re.match(pattern, stderr)1786 if match:1787 matched_strings[i] = str(match.group(1))1788 matches_found += 11789 if matches_found == total_patterns:1790 break1791 1792 return exe_path, matched_strings1793