diff options
author | Bill Wendling <isanbard@gmail.com> | 2013-06-19 07:59:39 +0000 |
---|---|---|
committer | Bill Wendling <isanbard@gmail.com> | 2013-06-19 07:59:39 +0000 |
commit | fbb8ee3faa48a19719a630a2f01f6ace3331a43e (patch) | |
tree | 1dd191f123f264fa263eedbe3eb0adbd81ea5ace | |
parent | 7b2952d3b13a797d032f38d4e715c16dfc7ce8a3 (diff) | |
download | llvm-fbb8ee3faa48a19719a630a2f01f6ace3331a43e.zip llvm-fbb8ee3faa48a19719a630a2f01f6ace3331a43e.tar.gz llvm-fbb8ee3faa48a19719a630a2f01f6ace3331a43e.tar.bz2 |
Merge revisions r181919:183444 except for r182099, r182650, and r182683.
llvm-svn: 184287
256 files changed, 10200 insertions, 1507 deletions
diff --git a/lldb/.arcconfig b/lldb/.arcconfig new file mode 100644 index 0000000..27072d6 --- /dev/null +++ b/lldb/.arcconfig @@ -0,0 +1,4 @@ +{ + "project_id" : "lldb", + "conduit_uri" : "http://llvm-reviews.chandlerc.com/" +} diff --git a/lldb/CMakeLists.txt b/lldb/CMakeLists.txt index 33ecc0e..f9efaa92 100644 --- a/lldb/CMakeLists.txt +++ b/lldb/CMakeLists.txt @@ -147,6 +147,11 @@ if (CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR) "`CMakeFiles'. Please delete them.")
endif()
+# Compute the LLDB version from the LLVM version.
+string(REGEX MATCH "[0-9]+\\.[0-9]+(\\.[0-9]+)?" LLDB_VERSION
+ ${PACKAGE_VERSION})
+message(STATUS "LLDB version: ${LLDB_VERSION}")
+
macro(add_lldb_library name)
llvm_process_sources(srcs ${ARGN})
if (MSVC_IDE OR XCODE)
@@ -170,7 +175,7 @@ macro(add_lldb_library name) #endif()
if(LLDB_USED_LIBS)
- if (CMAKE_SYSTEM_NAME MATCHES "Linux")
+ if (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
target_link_libraries(${name} -Wl,--start-group ${LLDB_USED_LIBS} -Wl,--end-group)
else()
target_link_libraries(${name} ${LLDB_USED_LIBS})
@@ -241,7 +246,13 @@ if (CMAKE_SYSTEM_NAME MATCHES "Darwin") ${DEBUG_SYMBOLS_LIBRARY})
endif()
+# On FreeBSD, link libexecinfo because libc is missing backtrace()
+if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
+ list(APPEND system_libs execinfo)
+endif()
+
#add_subdirectory(include)
+add_subdirectory(docs)
add_subdirectory(scripts)
add_subdirectory(source)
add_subdirectory(test)
diff --git a/lldb/docs/CMakeLists.txt b/lldb/docs/CMakeLists.txt new file mode 100644 index 0000000..af7013d --- /dev/null +++ b/lldb/docs/CMakeLists.txt @@ -0,0 +1,41 @@ + +include(FindDoxygen) + +if(DOXYGEN_FOUND) + set(abs_top_srcdir ${CMAKE_CURRENT_SOURCE_DIR}/..) + set(DOT dot) + set(PACKAGE_VERSION mainline) + set(abs_top_builddir ..) + configure_file(${CMAKE_CURRENT_SOURCE_DIR}/doxygen.cfg.in + ${CMAKE_CURRENT_BINARY_DIR}/doxygen.cfg @ONLY) + + add_custom_target(lldb-cpp-doc + ${DOXYGEN_EXECUTABLE} ${CMAKE_CURRENT_BINARY_DIR}/doxygen.cfg + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMENT "Generating LLDB C++ API reference with Doxygen" VERBATIM + ) +endif(DOXYGEN_FOUND) + +find_package(PythonInterp REQUIRED) +find_program(EPYDOC_EXECUTABLE NAMES epydoc epydoc.py) +if(EPYDOC_EXECUTABLE) + find_program(DOT_EXECUTABLE dot) + if(DOT_EXECUTABLE) + set(EPYDOC_OPTIONS ${EPYDOC_OPTIONS} --graph all --dotpath ${DOT_EXECUTABLE}) + endif() + set(DOC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/doc") + file(MAKE_DIRECTORY "${DOC_DIR}") + #set(ENV{PYTHONPATH} ${CMAKE_CURRENT_BINARY_DIR}/../../../lib/python2.7/site-packages) + add_custom_target(lldb-python-doc + ${EPYDOC_EXECUTABLE} + --html + lldb + -o ${CMAKE_CURRENT_BINARY_DIR}/python_reference + --name "LLDB python API" + --url "http://lldb.llvm.org" + ${EPYDOC_OPTIONS} + DEPENDS swig_wrapper liblldb + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/../../../lib/python2.7/site-packages + COMMENT "Generating LLDB Python API reference with epidoc" VERBATIM + ) +endif(EPYDOC_EXECUTABLE) diff --git a/lldb/docs/doxygen.cfg.in b/lldb/docs/doxygen.cfg.in new file mode 100644 index 0000000..b73f890 --- /dev/null +++ b/lldb/docs/doxygen.cfg.in @@ -0,0 +1,1633 @@ +# Doxyfile 1.7.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project +# +# All text after a hash (#) is considered a comment and will be ignored +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" ") + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded +# by quotes) that should identify the project. + +PROJECT_NAME = LLVM + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = @PACKAGE_VERSION@ + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = @abs_top_builddir@/docs/cpp_reference + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrilic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = ../.. + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful is your file systems +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 2 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen to replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penality. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will rougly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols + +SYMBOL_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = YES + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespace are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or define consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and defines in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# If the sources in your project are distributed over multiple directories +# then setting the SHOW_DIRECTORIES tag to YES will show the directory hierarchy +# in the documentation. The default is NO. + +SHOW_DIRECTORIES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command <command> <input-file>, where <command> is the value of +# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. The create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = NO + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = NO + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be abled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = @abs_top_srcdir@/include \ + @abs_top_srcdir@/scripts/Python/interface \ + @abs_top_srcdir@/source \ + @abs_top_srcdir@/docs/doxygen.intro + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh *.hxx +# *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.py *.f90 + +FILE_PATTERNS = + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used select whether or not files or +# directories that are symbolic links (a Unix filesystem feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = @abs_top_srcdir@/examples + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = YES + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = @abs_top_srcdir@/docs/img + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty, INPUT_FILTER +# is applied to all files. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = YES + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C and C++ comments will always remain visible. + +STRIP_CODE_COMMENTS = NO + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = YES + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = YES + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 4 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = llvm:: + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. + +HTML_HEADER = @abs_top_srcdir@/docs/doxygen.header + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = @abs_top_srcdir@/docs/doxygen.footer + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# stylesheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = @abs_top_srcdir@/../../docs/doxygen.css + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the stylesheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = YES + +# If the HTML_ALIGN_MEMBERS tag is set to YES, the members of classes, +# files or namespaces will be aligned in HTML using tables. If set to +# NO a bullet list will be used. + +HTML_ALIGN_MEMBERS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. For this to work a browser that supports +# JavaScript and DHTML is required (for instance Mozilla 1.0+, Firefox +# Netscape 6.0+, Internet explorer 5.0+, Konqueror, or Safari). + +HTML_DYNAMIC_SECTIONS = NO + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters"> +# Qt Help Project / Custom Filters</a>. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes"> +# Qt Help Project / Filter Attributes</a>. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index at +# top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. + +DISABLE_INDEX = NO + +# This tag can be used to set the number of enum values (range [1..20]) +# that doxygen will group on one line in the generated HTML documentation. + +ENUM_VALUES_PER_LINE = 4 + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. + +GENERATE_TREEVIEW = NO + +# By enabling USE_INLINE_TREES, doxygen will generate the Groups, Directories, +# and Class Hierarchy pages using a tree view instead of an ordered list. + +USE_INLINE_TREES = NO + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = NO + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvances is that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, a4wide, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = letter + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = NO + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = NO + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load stylesheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# in the INCLUDE_PATH (see below) will be search if a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = ../scripts/Python/interface + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all function-like macros that are alone +# on a line, have an all uppercase name, and do not end with a semicolon. Such +# function macros are typically used for boiler-plate code, and will confuse +# the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. +# Optionally an initial location of the external documentation +# can be added for each tagfile. The format of a tag file without +# this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths or +# URLs. If a location is present for each tag, the installdox tool +# does not have to be run to correct the links. +# Note that each tag file must have a unique name +# (where the name does NOT include the path) +# If a tag file is not located in the directory in which doxygen +# is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = YES + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option is superseded by the HAVE_DOT option below. This is only a +# fallback. It is recommended to install and use dot, since it yields more +# powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = NO + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will write a font called FreeSans.ttf to the output +# directory and reference it in all dot files that doxygen generates. This +# font does not include all possible unicode characters however, so when you need +# these (or just want a differently looking font) you can specify the font name +# using DOT_FONTNAME. You need need to make sure dot is able to find the font, +# which can be done by putting it in a standard location or by setting the +# DOTFONTPATH environment variable or by setting DOT_FONTPATH to the directory +# containing the font. + +DOT_FONTNAME = FreeSans + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the output directory to look for the +# FreeSans.ttf font (which doxygen will put there itself). If you specify a +# different font using DOT_FONTNAME you can set the path where dot +# can find it using this tag. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# the CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = YES + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH, SHOW_DIRECTORIES and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are png, jpg, or gif +# If left blank png will be used. + +DOT_IMAGE_FORMAT = png + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = @DOT@ + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = YES + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = NO + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES diff --git a/lldb/docs/doxygen.footer b/lldb/docs/doxygen.footer new file mode 100644 index 0000000..c14814b --- /dev/null +++ b/lldb/docs/doxygen.footer @@ -0,0 +1,13 @@ +<hr> +<p class="footer"> +Generated on $datetime for <a href="http://lldb.llvm.org/">$projectname</a> by +<a href="http://www.doxygen.org"><img src="doxygen.png" alt="Doxygen" +align="middle" border="0"/>$doxygenversion</a><br> +Copyright © 2003-2013 University of Illinois at Urbana-Champaign. +All Rights Reserved.</p> + +<hr> +<!--#include virtual="/attrib.incl" --> + +</body> +</html> diff --git a/lldb/docs/doxygen.header b/lldb/docs/doxygen.header new file mode 100644 index 0000000..e6e0331 --- /dev/null +++ b/lldb/docs/doxygen.header @@ -0,0 +1,9 @@ +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> +<html><head> +<meta http-equiv="Content-Type" content="text/html;charset=iso-8859-1"/> +<meta name="keywords" content="LLDB,C++,doxygen,API,documentation"/> +<meta name="description" content="C++ source code API documentation for LLDB."/> +<title>LLVM: $title</title> +<link href="doxygen.css" rel="stylesheet" type="text/css"/> +</head><body> +<p class="title">LLDB API Documentation</p> diff --git a/lldb/docs/doxygen.intro b/lldb/docs/doxygen.intro new file mode 100644 index 0000000..2ad0ccb --- /dev/null +++ b/lldb/docs/doxygen.intro @@ -0,0 +1,19 @@ +/// @mainpage LLDB +/// +/// @section main_intro Introduction +/// Welcome to LLDB. +/// +/// This documentation describes the @b interface that can drive LLDB. +/// There are no instructions here on how to use LLDB, only the APIs +/// that make up the software. For usage instructions, please see +/// the help command. +/// +/// @section main_caveat Caveat +/// This documentation is generated directly from the source code with doxygen. +/// Since LLDB is constantly under active development, what you're about to +/// read is out of date! However, it may still be useful since certain portions +/// of LLDB are very stable. +/// +/// @section main_changelog Change Log +/// - Adapted for LLDB 05/25 by Daniel Malea +/// - Original content written 12/30/2003 by Reid Spencer diff --git a/lldb/examples/python/diagnose_nsstring.py b/lldb/examples/python/diagnose_nsstring.py new file mode 100644 index 0000000..aca5c7f --- /dev/null +++ b/lldb/examples/python/diagnose_nsstring.py @@ -0,0 +1,171 @@ +# This implements the "diagnose-nsstring" command, usually installed in the debug session like +# command script import lldb.diagnose +# it is used when NSString summary formatter fails to replicate the logic that went into LLDB making the +# decisions it did and providing some useful context information that can be used for improving the formatter + +import lldb + +def read_memory(process,location,size): + data = "" + error = lldb.SBError() + for x in range(0,size-1): + byte = process.ReadUnsignedFromMemory(x+location,1,error) + if error.fail: + data = data + "err%s" % "" if x == size-2 else ":" + else: + try: + data = data + "0x%x" % byte + if byte == 0: + data = data + "(\\0)" + elif byte == 0xa: + data = data + "(\\a)" + elif byte == 0xb: + data = data + "(\\b)" + elif byte == 0xc: + data = data + "(\\c)" + elif byte == '\n': + data = data + "(\\n)" + else: + data = data + "(%s)" % chr(byte) + if x < size-2: + data = data + ":" + except Exception as e: + print e + return data + +def diagnose_nsstring_Command_Impl(debugger,command,result,internal_dict): + """ + A command to diagnose the LLDB NSString data formatter + invoke as + (lldb) diagnose-nsstring <expr returning NSString> + e.g. + (lldb) diagnose-nsstring @"Hello world" + """ + target = debugger.GetSelectedTarget() + process = target.GetProcess() + thread = process.GetSelectedThread() + frame = thread.GetSelectedFrame() + if not target.IsValid() or not process.IsValid(): + return "unable to get target/process - cannot proceed" + options = lldb.SBExpressionOptions() + options.SetFetchDynamicValue() + error = lldb.SBError() + if frame.IsValid(): + nsstring = frame.EvaluateExpression(command,options) + else: + nsstring = target.EvaluateExpression(command,options) + print >>result,str(nsstring) + nsstring_address = nsstring.GetValueAsUnsigned(0) + if nsstring_address == 0: + return "unable to obtain the string - cannot proceed" + expression = "\ +struct $__lldb__notInlineMutable {\ + char* buffer;\ + signed long length;\ + signed long capacity;\ + unsigned int hasGap:1;\ + unsigned int isFixedCapacity:1;\ + unsigned int isExternalMutable:1;\ + unsigned int capacityProvidedExternally:1;\n\ +#if __LP64__\n\ + unsigned long desiredCapacity:60;\n\ +#else\n\ + unsigned long desiredCapacity:28;\n\ +#endif\n\ + void* contentsAllocator;\ +};\ +\ +struct $__lldb__CFString {\ + void* _cfisa;\ + uint8_t _cfinfo[4];\ + uint32_t _rc;\ + union {\ + struct __inline1 {\ + signed long length;\ + } inline1;\ + struct __notInlineImmutable1 {\ + char* buffer;\ + signed long length;\ + void* contentsDeallocator;\ + } notInlineImmutable1;\ + struct __notInlineImmutable2 {\ + char* buffer;\ + void* contentsDeallocator;\ + } notInlineImmutable2;\ + struct $__lldb__notInlineMutable notInlineMutable;\ + } variants;\ +};\ +" + + expression = expression + "*(($__lldb__CFString*) %d)" % nsstring_address + # print expression + dumped = target.EvaluateExpression(expression,options) + print >>result, str(dumped) + + little_endian = (target.byte_order == lldb.eByteOrderLittle) + ptr_size = target.addr_size + + info_bits = dumped.GetChildMemberWithName("_cfinfo").GetChildAtIndex(0 if little_endian else 3).GetValueAsUnsigned(0) + is_mutable = (info_bits & 1) == 1 + is_inline = (info_bits & 0x60) == 0 + has_explicit_length = (info_bits & (1 | 4)) != 4 + is_unicode = (info_bits & 0x10) == 0x10 + is_special = (nsstring.GetDynamicValue(lldb.eDynamicCanRunTarget).GetTypeName() == "NSPathStore2") + has_null = (info_bits & 8) == 8 + + print >>result,"\nInfo=%d\nMutable=%s\nInline=%s\nExplicit=%s\nUnicode=%s\nSpecial=%s\nNull=%s\n" % \ + (info_bits, "yes" if is_mutable else "no","yes" if is_inline else "no","yes" if has_explicit_length else "no","yes" if is_unicode else "no","yes" if is_special else "no","yes" if has_null else "no") + + + explicit_length_offset = 0 + if not has_null and has_explicit_length and not is_special: + explicit_length_offset = 2*ptr_size + if is_mutable and not is_inline: + explicit_length_offset = explicit_length_offset + ptr_size + elif is_inline: + pass + elif not is_inline and not is_mutable: + explicit_length_offset = explicit_length_offset + ptr_size + else: + explicit_length_offset = 0 + + if explicit_length_offset == 0: + print >>result,"There is no explicit length marker - skipping this step\n" + else: + explicit_length_offset = nsstring_address + explicit_length_offset + explicit_length = process.ReadUnsignedFromMemory(explicit_length_offset, 4, error) + print >>result,"Explicit length location is at 0x%x - read value is %d\n" % (explicit_length_offset,explicit_length) + + if is_mutable: + location = 2 * ptr_size + nsstring_address + location = process.ReadPointerFromMemory(location,error) + elif is_inline and has_explicit_length and not is_unicode and not is_special and not is_mutable: + location = 3 * ptr_size + nsstring_address + elif is_unicode: + location = 2 * ptr_size + nsstring_address + if is_inline: + if not has_explicit_length: + print >>result,"Unicode & Inline & !Explicit is a new combo - no formula for it" + else: + location += ptr_size + else: + location = process.ReadPointerFromMemory(location,error) + elif is_special: + location = nsstring_address + ptr_size + 4 + elif is_inline: + location = 2 * ptr_size + nsstring_address + if not has_explicit_length: + location += 1 + else: + location = 2 * ptr_size + nsstring_address + location = process.ReadPointerFromMemory(location,error) + print >>result,"Expected data location: 0x%x\n" % (location) + print >>result,"1K of data around location: %s\n" % read_memory(process,location,1024) + print >>result,"5K of data around string pointer: %s\n" % read_memory(process,nsstring_address,1024*5) + +def __lldb_init_module(debugger, internal_dict): + debugger.HandleCommand("command script add -f %s.diagnose_nsstring_Command_Impl diagnose-nsstring" % __name__) + print 'The "diagnose-nsstring" command has been installed, type "help diagnose-nsstring" for detailed help.' + +__lldb_init_module(lldb.debugger,None) +__lldb_init_module = None
\ No newline at end of file diff --git a/lldb/examples/python/diagnose_unwind.py b/lldb/examples/python/diagnose_unwind.py index 379fab9..ab95d23 100644 --- a/lldb/examples/python/diagnose_unwind.py +++ b/lldb/examples/python/diagnose_unwind.py @@ -1,8 +1,10 @@ -# This implements the "diagnose-unwind" command, usually installed in the debug session like -# script import lldb.macosx -# it is used when lldb's backtrace fails -- it collects and prints information about the stack frames, -# and tries an alternate unwind algorithm, that will help to understand why lldb's unwind algorithm did -# not succeed. +# This implements the "diagnose-unwind" command, usually installed +# in the debug session like +# command script import lldb.diagnose +# it is used when lldb's backtrace fails -- it collects and prints +# information about the stack frames, and tries an alternate unwind +# algorithm, that will help to understand why lldb's unwind algorithm +# did not succeed. import optparse import lldb @@ -88,8 +90,14 @@ def simple_backtrace(debugger): backtrace_print_frame (target, frame_num, cur_pc, cur_fp) def diagnose_unwind(debugger, command, result, dict): - # Use the Shell Lexer to properly parse up command options just like a - # shell would + """ +Gather diagnostic information to help debug incorrect unwind (backtrace) +behavior in lldb. When there is a backtrace that doesn't look +correct, run this command with the correct thread selected and a +large amount of diagnostic information will be printed, it is likely +to be helpful when reporting the problem. + """ + command_args = shlex.split(command) parser = create_diagnose_unwind_options() try: @@ -110,6 +118,7 @@ def diagnose_unwind(debugger, command, result, dict): if len(lldb_versions_match.groups()) >= 5 and lldb_versions_match.groups()[4]: lldb_minor = int(lldb_versions_match.groups()[4]) + print 'LLDB version %s' % debugger.GetVersionString() print 'Unwind diagnostics for thread %d' % thread.GetIndexID() print "" print "lldb's unwind algorithm:" diff --git a/lldb/include/lldb/API/SBDebugger.h b/lldb/include/lldb/API/SBDebugger.h index 2de86d6..518cbf6 100644 --- a/lldb/include/lldb/API/SBDebugger.h +++ b/lldb/include/lldb/API/SBDebugger.h @@ -172,6 +172,12 @@ public: bool GetUseExternalEditor (); + bool + SetUseColor (bool use_color); + + bool + GetUseColor () const; + static bool GetDefaultArchitecture (char *arch_name, size_t arch_name_len); diff --git a/lldb/include/lldb/API/SBDefines.h b/lldb/include/lldb/API/SBDefines.h index e8740a8..2388c36 100644 --- a/lldb/include/lldb/API/SBDefines.h +++ b/lldb/include/lldb/API/SBDefines.h @@ -19,6 +19,7 @@ #include "lldb/lldb-enumerations.h" #include "lldb/lldb-forward.h" #include "lldb/lldb-types.h" +#include "lldb/lldb-versioning.h" // Forward Declarations diff --git a/lldb/include/lldb/Breakpoint/BreakpointLocation.h b/lldb/include/lldb/Breakpoint/BreakpointLocation.h index efdc138..9ab0a79 100644 --- a/lldb/include/lldb/Breakpoint/BreakpointLocation.h +++ b/lldb/include/lldb/Breakpoint/BreakpointLocation.h @@ -20,10 +20,11 @@ // Project includes #include "lldb/lldb-private.h" #include "lldb/Breakpoint/StoppointLocation.h" -#include "lldb/Core/UserID.h" #include "lldb/Core/Address.h" -#include "lldb/Target/Process.h" #include "lldb/Core/StringList.h" +#include "lldb/Core/UserID.h" +#include "lldb/Host/Mutex.h" +#include "lldb/Target/Process.h" #include "lldb/Expression/ClangUserExpression.h" namespace lldb_private { @@ -386,6 +387,7 @@ private: std::unique_ptr<BreakpointOptions> m_options_ap; ///< Breakpoint options pointer, NULL if we're using our breakpoint's options. lldb::BreakpointSiteSP m_bp_site_sp; ///< Our breakpoint site (it may be shared by more than one location.) ClangUserExpression::ClangUserExpressionSP m_user_expression_sp; ///< The compiled expression to use in testing our condition. + Mutex m_condition_mutex; ///< Guards parsing and evaluation of the condition, which could be evaluated by multiple processes. size_t m_condition_hash; ///< For testing whether the condition source code changed. void diff --git a/lldb/include/lldb/Core/Debugger.h b/lldb/include/lldb/Core/Debugger.h index 34e939d..bed93fe 100644 --- a/lldb/include/lldb/Core/Debugger.h +++ b/lldb/include/lldb/Core/Debugger.h @@ -236,7 +236,6 @@ public: const ExecutionContext *exe_ctx, const Address *addr, Stream &s, - const char **end, ValueObject* valobj = NULL); @@ -308,6 +307,12 @@ public: bool SetUseExternalEditor (bool use_external_editor_p); + bool + GetUseColor () const; + + bool + SetUseColor (bool use_color); + uint32_t GetStopSourceLineCount (bool before) const; diff --git a/lldb/include/lldb/Core/Module.h b/lldb/include/lldb/Core/Module.h index 7ba9856..79da797 100644 --- a/lldb/include/lldb/Core/Module.h +++ b/lldb/include/lldb/Core/Module.h @@ -595,7 +595,9 @@ public: IsLoadedInTarget (Target *target); bool - LoadScriptingResourceInTarget (Target *target, Error& error); + LoadScriptingResourceInTarget (Target *target, + Error& error, + Stream* feedback_stream = NULL); //------------------------------------------------------------------ /// Get the number of compile units for this module. diff --git a/lldb/include/lldb/Core/ModuleList.h b/lldb/include/lldb/Core/ModuleList.h index 46b2468..8da07cb 100644 --- a/lldb/include/lldb/Core/ModuleList.h +++ b/lldb/include/lldb/Core/ModuleList.h @@ -11,6 +11,7 @@ #define liblldb_ModuleList_h_ #include <vector> +#include <list> #include "lldb/lldb-private.h" #include "lldb/Host/Mutex.h" @@ -484,6 +485,12 @@ public: size_t GetSize () const; + bool + LoadScriptingResourcesInTarget (Target *target, + std::list<Error>& errors, + Stream* feedback_stream = NULL, + bool continue_on_error = true); + static bool ModuleIsInCache (const Module *module_ptr); diff --git a/lldb/include/lldb/Core/Scalar.h b/lldb/include/lldb/Core/Scalar.h index 2a89a59..821a0fb 100644 --- a/lldb/include/lldb/Core/Scalar.h +++ b/lldb/include/lldb/Core/Scalar.h @@ -102,6 +102,9 @@ public: bool Cast (Scalar::Type type); + + bool + MakeSigned (); static const char * GetValueTypeAsCString (Scalar::Type value_type); diff --git a/lldb/include/lldb/Core/UUID.h b/lldb/include/lldb/Core/UUID.h index a2c7038..86ba099 100644 --- a/lldb/include/lldb/Core/UUID.h +++ b/lldb/include/lldb/Core/UUID.h @@ -22,7 +22,8 @@ namespace lldb_private { class UUID { public: - typedef uint8_t ValueType[16]; + // Most UUIDs are 16 bytes, but some Linux build-ids (SHA1) are 20. + typedef uint8_t ValueType[20]; //------------------------------------------------------------------ // Constructors and Destructors @@ -45,20 +46,20 @@ public: const void * GetBytes() const; - static size_t + size_t GetByteSize(); bool IsValid () const; void - SetBytes (const void *uuid_bytes); + SetBytes (const void *uuid_bytes, uint32_t num_uuid_bytes = 16); std::string GetAsString () const; size_t - SetFromCString (const char *c_str); + SetFromCString (const char *c_str, uint32_t num_uuid_bytes = 16); // Decode as many UUID bytes (up to 16) as possible from the C string "cstr" // This is used for auto completion where a partial UUID might have been @@ -86,12 +87,13 @@ public: /// which should be 16 if a full UUID value was properly decoded. //------------------------------------------------------------------ static size_t - DecodeUUIDBytesFromCString (const char *cstr, ValueType &uuid_bytes, const char **end); + DecodeUUIDBytesFromCString (const char *cstr, ValueType &uuid_bytes, const char **end, uint32_t num_uuid_bytes = 16); protected: //------------------------------------------------------------------ // Classes that inherit from UUID can see and modify these //------------------------------------------------------------------ + uint32_t m_num_uuid_bytes; // Should be 16 or 20 ValueType m_uuid; }; diff --git a/lldb/include/lldb/Core/ValueObject.h b/lldb/include/lldb/Core/ValueObject.h index 5fb4c15..49c6198 100644 --- a/lldb/include/lldb/Core/ValueObject.h +++ b/lldb/include/lldb/Core/ValueObject.h @@ -1104,7 +1104,7 @@ public: } //------------------------------------------------------------------ - /// Find out if a SBValue might have children. + /// Find out if a ValueObject might have children. /// /// This call is much more efficient than CalculateNumChildren() as /// it doesn't need to complete the underlying type. This is designed @@ -1116,7 +1116,7 @@ public: /// doing any expensive type completion. /// /// @return - /// Returns \b true if the SBValue might have children, or \b + /// Returns \b true if the ValueObject might have children, or \b /// false otherwise. //------------------------------------------------------------------ virtual bool @@ -1230,6 +1230,7 @@ protected: // as an independent ValueObjectConstResult, which isn't managed by us. lldb::Format m_format; + lldb::Format m_last_format; uint32_t m_last_format_mgr_revision; lldb::TypeSummaryImplSP m_type_summary_sp; lldb::TypeFormatImplSP m_type_format_sp; diff --git a/lldb/include/lldb/Expression/IRExecutionUnit.h b/lldb/include/lldb/Expression/IRExecutionUnit.h index 9bc5592..3f2d08a 100644 --- a/lldb/include/lldb/Expression/IRExecutionUnit.h +++ b/lldb/include/lldb/Expression/IRExecutionUnit.h @@ -336,7 +336,13 @@ private: /// @return /// True in case of failure, false in case of success. //------------------------------------------------------------------ - bool applyPermissions(std::string *ErrMsg) { return false; } + virtual bool applyPermissions(std::string *ErrMsg) { + // TODO: Ensure that the instruction cache is flushed because + // relocations are updated by dy-load. See: + // sys::Memory::InvalidateInstructionCache + // llvm::SectionMemoryManager + return false; + } //------------------------------------------------------------------ /// Passthrough interface stub diff --git a/lldb/include/lldb/Expression/IRMemoryMap.h b/lldb/include/lldb/Expression/IRMemoryMap.h index 2e8bdb4..affe193 100644 --- a/lldb/include/lldb/Expression/IRMemoryMap.h +++ b/lldb/include/lldb/Expression/IRMemoryMap.h @@ -50,6 +50,7 @@ public: }; lldb::addr_t Malloc (size_t size, uint8_t alignment, uint32_t permissions, AllocationPolicy policy, Error &error); + void Leak (lldb::addr_t process_address, Error &error); void Free (lldb::addr_t process_address, Error &error); void WriteMemory (lldb::addr_t process_address, const uint8_t *bytes, size_t size, Error &error); @@ -84,7 +85,10 @@ private: uint32_t m_permissions; ///< The access permissions on the memory in the process. In the host, the memory is always read/write. uint8_t m_alignment; ///< The alignment of the requested allocation DataBufferHeap m_data; + + ///< Flags AllocationPolicy m_policy; + bool m_leak; public: Allocation (lldb::addr_t process_alloc, lldb::addr_t process_start, @@ -100,7 +104,8 @@ private: m_permissions (0), m_alignment (0), m_data (), - m_policy (eAllocationPolicyInvalid) + m_policy (eAllocationPolicyInvalid), + m_leak (false) { } }; diff --git a/lldb/include/lldb/Expression/Materializer.h b/lldb/include/lldb/Expression/Materializer.h index cb61c88..f6b5691 100644 --- a/lldb/include/lldb/Expression/Materializer.h +++ b/lldb/include/lldb/Expression/Materializer.h @@ -155,7 +155,6 @@ private: typedef std::unique_ptr<Entity> EntityUP; typedef std::vector<EntityUP> EntityVector; - unsigned m_result_index; DematerializerWP m_dematerializer_wp; EntityVector m_entities; Entity *m_result_entity; diff --git a/lldb/include/lldb/Host/File.h b/lldb/include/lldb/Host/File.h index a0e6257..df7fe92 100644 --- a/lldb/include/lldb/Host/File.h +++ b/lldb/include/lldb/Host/File.h @@ -282,17 +282,19 @@ public: /// @see File::Read (void *, size_t, off_t &) /// @see File::Write (const void *, size_t, off_t &) /// - /// @param[in/out] offset + /// @param[in] offset /// The offset to seek to within the file relative to the - /// beginning of the file which gets filled in the the resulting - /// absolute file offset. + /// beginning of the file. + /// + /// @param[in] error_ptr + /// A pointer to a lldb_private::Error object that will be + /// filled in if non-NULL. /// /// @return - /// An error object that indicates success or the reason for - /// failure. + /// The resulting seek offset, or -1 on error. //------------------------------------------------------------------ - Error - SeekFromStart (off_t& offset); + off_t + SeekFromStart (off_t offset, Error *error_ptr = NULL); //------------------------------------------------------------------ /// Seek to an offset relative to the current file position. @@ -303,17 +305,19 @@ public: /// @see File::Read (void *, size_t, off_t &) /// @see File::Write (const void *, size_t, off_t &) /// - /// @param[in/out] offset + /// @param[in] offset /// The offset to seek to within the file relative to the - /// current file position. On return this parameter gets filled - /// in the the resulting absolute file offset. + /// current file position. + /// + /// @param[in] error_ptr + /// A pointer to a lldb_private::Error object that will be + /// filled in if non-NULL. /// /// @return - /// An error object that indicates success or the reason for - /// failure. + /// The resulting seek offset, or -1 on error. //------------------------------------------------------------------ - Error - SeekFromCurrent (off_t& offset); + off_t + SeekFromCurrent (off_t offset, Error *error_ptr = NULL); //------------------------------------------------------------------ /// Seek to an offset relative to the end of the file. @@ -329,12 +333,15 @@ public: /// end of the file which gets filled in the the resulting /// absolute file offset. /// + /// @param[in] error_ptr + /// A pointer to a lldb_private::Error object that will be + /// filled in if non-NULL. + /// /// @return - /// An error object that indicates success or the reason for - /// failure. + /// The resulting seek offset, or -1 on error. //------------------------------------------------------------------ - Error - SeekFromEnd (off_t& offset); + off_t + SeekFromEnd (off_t offset, Error *error_ptr = NULL); //------------------------------------------------------------------ /// Read bytes from a file from the specified file offset. diff --git a/lldb/include/lldb/Host/Host.h b/lldb/include/lldb/Host/Host.h index 90fbb0a..1fcb0da 100644 --- a/lldb/include/lldb/Host/Host.h +++ b/lldb/include/lldb/Host/Host.h @@ -13,6 +13,7 @@ #include <stdarg.h> +#include <map> #include <string> #include "lldb/lldb-private.h" @@ -414,7 +415,12 @@ public: static uint32_t FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstanceInfoList &proc_infos); - + + typedef std::map<lldb::pid_t, bool> TidMap; + typedef std::pair<lldb::pid_t, bool> TidPair; + static bool + FindProcessThreads (const lldb::pid_t pid, TidMap &tids_to_attach); + static bool GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &proc_info); diff --git a/lldb/include/lldb/Interpreter/CommandInterpreter.h b/lldb/include/lldb/Interpreter/CommandInterpreter.h index 15911b4..a2155882 100644 --- a/lldb/include/lldb/Interpreter/CommandInterpreter.h +++ b/lldb/include/lldb/Interpreter/CommandInterpreter.h @@ -396,7 +396,9 @@ public: void FindCommandsForApropos (const char *word, StringList &commands_found, - StringList &commands_help); + StringList &commands_help, + bool search_builtin_commands, + bool search_user_commands); bool GetBatchCommandMode () { return m_batch_command_mode; } diff --git a/lldb/include/lldb/Interpreter/PythonDataObjects.h b/lldb/include/lldb/Interpreter/PythonDataObjects.h index f978b68..3893e3b 100644 --- a/lldb/include/lldb/Interpreter/PythonDataObjects.h +++ b/lldb/include/lldb/Interpreter/PythonDataObjects.h @@ -82,13 +82,16 @@ namespace lldb_private { } void - Dump () const + Dump () const { if (m_py_obj) _PyObject_Dump (m_py_obj); else puts ("NULL"); } + + void + Dump (Stream &strm) const; PyObject* GetPythonObject () const diff --git a/lldb/include/lldb/Symbol/SymbolContext.h b/lldb/include/lldb/Symbol/SymbolContext.h index 90fdb7f..4ba9cd9 100644 --- a/lldb/include/lldb/Symbol/SymbolContext.h +++ b/lldb/include/lldb/Symbol/SymbolContext.h @@ -153,7 +153,7 @@ public: /// information in this context. If a module, function, file and /// line number are available, they will be dumped. If only a /// module and function or symbol name with offset is available, - /// that will be ouput. Else just the address at which the target + /// that will be output. Else just the address at which the target /// was stopped will be displayed. /// /// @param[in] s @@ -285,7 +285,31 @@ public: /// The name of the function represented by this symbol context. //------------------------------------------------------------------ ConstString - GetFunctionName (Mangled::NamePreference preference = Mangled::ePreferDemangled); + GetFunctionName (Mangled::NamePreference preference = Mangled::ePreferDemangled) const; + + + //------------------------------------------------------------------ + /// Get the line entry that corresponds to the function. + /// + /// If the symbol context contains an inlined block, the line entry + /// for the start address of the inlined function will be returned, + /// otherwise the line entry for the start address of the function + /// will be returned. This can be used after doing a + /// Module::FindFunctions(...) or ModuleList::FindFunctions(...) + /// call in order to get the correct line table information for + /// the symbol context. + /// it will return the inlined function name. + /// + /// @param[in] prefer_mangled + /// if \btrue, then the mangled name will be returned if there + /// is one. Otherwise the unmangled name will be returned if it + /// is available. + /// + /// @return + /// The name of the function represented by this symbol context. + //------------------------------------------------------------------ + LineEntry + GetFunctionStartLineEntry () const; //------------------------------------------------------------------ /// Find the block containing the inlined block that contains this block. diff --git a/lldb/include/lldb/Target/CPPLanguageRuntime.h b/lldb/include/lldb/Target/CPPLanguageRuntime.h index bfa08e1..98a4ab8 100644 --- a/lldb/include/lldb/Target/CPPLanguageRuntime.h +++ b/lldb/include/lldb/Target/CPPLanguageRuntime.h @@ -86,7 +86,7 @@ public: return m_full; } - const ConstString & + llvm::StringRef GetBasename (); llvm::StringRef @@ -103,7 +103,7 @@ public: Parse(); ConstString m_full; // Full name: "lldb::SBTarget::GetBreakpointAtIndex(unsigned int) const" - ConstString m_basename; // Basename: "GetBreakpointAtIndex" + llvm::StringRef m_basename; // Basename: "GetBreakpointAtIndex" llvm::StringRef m_context; // Decl context: "lldb::SBTarget" llvm::StringRef m_arguments; // Arguments: "(unsigned int)" llvm::StringRef m_qualifiers; // Qualifiers: "const" @@ -134,9 +134,6 @@ public: IsCPPMangledName(const char *name); static bool - IsPossibleCPPCall (const char *name, const char *&base_name_start, const char *&base_name_end); - - static bool StripNamespacesFromVariableName (const char *name, const char *&base_name_start, const char *&base_name_end); // in some cases, compilers will output different names for one same type. when tht happens, it might be impossible diff --git a/lldb/include/lldb/Target/Memory.h b/lldb/include/lldb/Target/Memory.h index 504370e..568bbcd 100644 --- a/lldb/include/lldb/Target/Memory.h +++ b/lldb/include/lldb/Target/Memory.h @@ -38,7 +38,7 @@ namespace lldb_private { ~MemoryCache (); void - Clear(); + Clear(bool clear_invalid_ranges = false); void Flush (lldb::addr_t addr, size_t size); diff --git a/lldb/include/lldb/Target/Process.h b/lldb/include/lldb/Target/Process.h index 593c93b..b1f9eca2 100644 --- a/lldb/include/lldb/Target/Process.h +++ b/lldb/include/lldb/Target/Process.h @@ -3532,7 +3532,73 @@ public: else return m_public_run_lock; } - + + //------------------------------------------------------------------ + // This is a cache of reserved and available memory address ranges + // for a single modification ID (see m_mod_id). It's meant for use + // by IRMemoryMap, but to stick with the process. These memory + // ranges happen to be unallocated in the underlying process, but we + // make no guarantee that at a future modification ID they won't be + // gone. This is only useful if the underlying process can't + // allocate memory. + // + // When a memory space is determined to be available it is + // registered as reserved at the current modification. If it is + // freed later, it is added to the free list if the modification ID + // hasn't changed. Then clients can simply query the free list for + // the size they want. + //------------------------------------------------------------------ + class ReservationCache + { + public: + ReservationCache (Process &process); + + //------------------------------------------------------------------ + // Mark that a particular range of addresses is in use. Adds it + // to the reserved map, implicitly tying it to the current + // modification ID. + //------------------------------------------------------------------ + void + Reserve (lldb::addr_t addr, size_t size); + + //------------------------------------------------------------------ + // Mark that a range is no longer in use. If it's found in the + // reservation list, that means that the modification ID hasn't + // changed since it was reserved, so it can be safely added to the + // free list. + //------------------------------------------------------------------ + void + Unreserve (lldb::addr_t addr); + + //------------------------------------------------------------------ + // Try to find an unused range of the given size in the free list. + //------------------------------------------------------------------ + lldb::addr_t + Find (size_t size); + private: + //------------------------------------------------------------------ + // Clear all lists if the modification ID has changed. + //------------------------------------------------------------------ + void CheckModID(); + + typedef std::map <lldb::addr_t, size_t> ReservedMap; + typedef std::vector <lldb::addr_t> FreeList; + typedef std::map <size_t, FreeList> FreeMap; + + ReservedMap m_reserved_cache; + FreeMap m_free_cache; + + Process &m_process; + ProcessModID m_mod_id; + }; + + ReservationCache & + GetReservationCache () + { + return m_reservation_cache; + } +private: + ReservationCache m_reservation_cache; protected: //------------------------------------------------------------------ // NextEventAction provides a way to register an action on the next diff --git a/lldb/include/lldb/Target/Target.h b/lldb/include/lldb/Target/Target.h index 57e2270..908c7b5 100644 --- a/lldb/include/lldb/Target/Target.h +++ b/lldb/include/lldb/Target/Target.h @@ -12,6 +12,7 @@ // C Includes // C++ Includes +#include <list> // Other libraries and framework includes // Project includes @@ -45,7 +46,14 @@ typedef enum InlineStrategy eInlineBreakpointsHeaders, eInlineBreakpointsAlways } InlineStrategy; - + +typedef enum LoadScriptFromSymFile +{ + eLoadScriptFromSymFileTrue, + eLoadScriptFromSymFileFalse, + eLoadScriptFromSymFileWarn +} LoadScriptFromSymFile; + //---------------------------------------------------------------------- // TargetProperties //---------------------------------------------------------------------- @@ -119,6 +127,9 @@ public: uint32_t GetMaximumSizeOfStringSummary() const; + + uint32_t + GetMaximumMemReadSize () const; FileSpec GetStandardInputPath () const; @@ -147,12 +158,9 @@ public: bool GetUseFastStepping() const; - bool + LoadScriptFromSymFile GetLoadScriptFromSymbolFile() const; - - void - SetLoadScriptFromSymbolFile(bool b); - + }; typedef std::shared_ptr<TargetProperties> TargetPropertiesSP; @@ -730,6 +738,14 @@ public: void SetExecutableModule (lldb::ModuleSP& module_sp, bool get_dependent_files); + bool + LoadScriptingResources (std::list<Error>& errors, + Stream* feedback_stream = NULL, + bool continue_on_error = true) + { + return m_images.LoadScriptingResourcesInTarget(this,errors,feedback_stream,continue_on_error); + } + //------------------------------------------------------------------ /// Get accessor for the images for this process. /// diff --git a/lldb/include/lldb/Target/Thread.h b/lldb/include/lldb/Target/Thread.h index d0984c6..cc47dc7 100644 --- a/lldb/include/lldb/Target/Thread.h +++ b/lldb/include/lldb/Target/Thread.h @@ -381,7 +381,9 @@ public: virtual lldb::StackFrameSP GetFrameWithStackID (const StackID &stack_id) { - return GetStackFrameList()->GetFrameWithStackID (stack_id); + if (stack_id.IsValid()) + return GetStackFrameList()->GetFrameWithStackID (stack_id); + return lldb::StackFrameSP(); } uint32_t diff --git a/lldb/include/lldb/Target/ThreadPlan.h b/lldb/include/lldb/Target/ThreadPlan.h index 9c4259d..3d95a09 100644 --- a/lldb/include/lldb/Target/ThreadPlan.h +++ b/lldb/include/lldb/Target/ThreadPlan.h @@ -544,11 +544,11 @@ protected: return m_thread.GetPreviousPlan (this); } - // This forwards the private Thread::GetPrivateStopReason which is generally what + // This forwards the private Thread::GetPrivateStopInfo which is generally what // ThreadPlan's need to know. lldb::StopInfoSP - GetPrivateStopReason() + GetPrivateStopInfo() { return m_thread.GetPrivateStopInfo (); } diff --git a/lldb/include/lldb/Target/ThreadPlanCallFunction.h b/lldb/include/lldb/Target/ThreadPlanCallFunction.h index 2c1d142..d747706 100644 --- a/lldb/include/lldb/Target/ThreadPlanCallFunction.h +++ b/lldb/include/lldb/Target/ThreadPlanCallFunction.h @@ -115,12 +115,17 @@ public: // If the thread plan stops mid-course, this will be the stop reason that interrupted us. // Once DoTakedown is called, this will be the real stop reason at the end of the function call. + // If it hasn't been set for one or the other of these reasons, we'll return the PrivateStopReason. // This is needed because we want the CallFunction thread plans not to show up as the stop reason. // But if something bad goes wrong, it is nice to be able to tell the user what really happened. + virtual lldb::StopInfoSP GetRealStopInfo() { - return m_real_stop_info_sp; + if (m_real_stop_info_sp) + return m_real_stop_info_sp; + else + return GetPrivateStopInfo (); } lldb::addr_t diff --git a/lldb/include/lldb/Utility/AnsiTerminal.h b/lldb/include/lldb/Utility/AnsiTerminal.h index cf0d9f0..036950c 100644 --- a/lldb/include/lldb/Utility/AnsiTerminal.h +++ b/lldb/include/lldb/Utility/AnsiTerminal.h @@ -21,7 +21,7 @@ #define ANSI_BG_COLOR_BLACK 40 #define ANSI_BG_COLOR_RED 41 #define ANSI_BG_COLOR_GREEN 42 -#define ANSI_BG_COLOR_YELLOW 44 +#define ANSI_BG_COLOR_YELLOW 43 #define ANSI_BG_COLOR_BLUE 44 #define ANSI_BG_COLOR_PURPLE 45 #define ANSI_BG_COLOR_CYAN 46 @@ -82,5 +82,75 @@ namespace lldb_utility { const char *k_ctrl_conceal = "8"; const char *k_ctrl_crossed_out = "9"; + inline std::string + FormatAnsiTerminalCodes(const char *format, bool do_color = true) + { + // Convert "${ansi.XXX}" tokens to ansi values or clear them if do_color is false. + static const struct + { + const char *name; + const char *value; + } g_color_tokens[] = + { + #define _TO_STR2(_val) #_val + #define _TO_STR(_val) _TO_STR2(_val) + { "fg.black}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLACK) ANSI_ESC_END }, + { "fg.red}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_RED) ANSI_ESC_END }, + { "fg.green}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_GREEN) ANSI_ESC_END }, + { "fg.yellow}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_YELLOW) ANSI_ESC_END }, + { "fg.blue}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_BLUE) ANSI_ESC_END }, + { "fg.purple}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_PURPLE) ANSI_ESC_END }, + { "fg.cyan}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_CYAN) ANSI_ESC_END }, + { "fg.white}", ANSI_ESC_START _TO_STR(ANSI_FG_COLOR_WHITE) ANSI_ESC_END }, + { "bg.black}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLACK) ANSI_ESC_END }, + { "bg.red}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_RED) ANSI_ESC_END }, + { "bg.green}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_GREEN) ANSI_ESC_END }, + { "bg.yellow}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_YELLOW) ANSI_ESC_END }, + { "bg.blue}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_BLUE) ANSI_ESC_END }, + { "bg.purple}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_PURPLE) ANSI_ESC_END }, + { "bg.cyan}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_CYAN) ANSI_ESC_END }, + { "bg.white}", ANSI_ESC_START _TO_STR(ANSI_BG_COLOR_WHITE) ANSI_ESC_END }, + { "normal}", ANSI_ESC_START _TO_STR(ANSI_CTRL_NORMAL) ANSI_ESC_END }, + { "bold}", ANSI_ESC_START _TO_STR(ANSI_CTRL_BOLD) ANSI_ESC_END }, + { "faint}", ANSI_ESC_START _TO_STR(ANSI_CTRL_FAINT) ANSI_ESC_END }, + { "italic}", ANSI_ESC_START _TO_STR(ANSI_CTRL_ITALIC) ANSI_ESC_END }, + { "underline}", ANSI_ESC_START _TO_STR(ANSI_CTRL_UNDERLINE) ANSI_ESC_END }, + { "slow-blink}", ANSI_ESC_START _TO_STR(ANSI_CTRL_SLOW_BLINK) ANSI_ESC_END }, + { "fast-blink}", ANSI_ESC_START _TO_STR(ANSI_CTRL_FAST_BLINK) ANSI_ESC_END }, + { "negative}", ANSI_ESC_START _TO_STR(ANSI_CTRL_IMAGE_NEGATIVE) ANSI_ESC_END }, + { "conceal}", ANSI_ESC_START _TO_STR(ANSI_CTRL_CONCEAL) ANSI_ESC_END }, + { "crossed-out}", ANSI_ESC_START _TO_STR(ANSI_CTRL_CROSSED_OUT) ANSI_ESC_END }, + #undef _TO_STR + #undef _TO_STR2 + }; + static const char tok_hdr[] = "${ansi."; + + std::string fmt; + for (const char *p = format; *p; ++p) + { + const char *tok_start = strstr (p, tok_hdr); + if (!tok_start) + { + fmt.append (p, strlen(p)); + break; + } + + fmt.append (p, tok_start - p); + p = tok_start; + + const char *tok_str = tok_start + sizeof(tok_hdr) - 1; + for (size_t i = 0; i < sizeof(g_color_tokens) / sizeof(g_color_tokens[0]); ++i) + { + if (!strncmp (tok_str, g_color_tokens[i].name, strlen(g_color_tokens[i].name))) + { + if (do_color) + fmt.append (g_color_tokens[i].value); + p = tok_str + strlen (g_color_tokens[i].name) - 1; + break; + } + } + } + return fmt; + } } } diff --git a/lldb/include/lldb/lldb-enumerations.h b/lldb/include/lldb/lldb-enumerations.h index 307556a..cfc68f1 100644 --- a/lldb/include/lldb/lldb-enumerations.h +++ b/lldb/include/lldb/lldb-enumerations.h @@ -541,10 +541,7 @@ namespace lldb { // methods or selectors will be searched. eFunctionNameTypeMethod = (1u << 4), // Find function by method name (C++) with no namespace or arguments eFunctionNameTypeSelector = (1u << 5), // Find function by selector name (ObjC) names - eFunctionNameTypeAny = (eFunctionNameTypeFull | - eFunctionNameTypeBase | - eFunctionNameTypeMethod | - eFunctionNameTypeSelector ) + eFunctionNameTypeAny = eFunctionNameTypeAuto // DEPRECATED: use eFunctionNameTypeAuto } FunctionNameType; diff --git a/lldb/include/lldb/lldb-private-log.h b/lldb/include/lldb/lldb-private-log.h index 22fba7af..31a1c23 100644 --- a/lldb/include/lldb/lldb-private-log.h +++ b/lldb/include/lldb/lldb-private-log.h @@ -43,6 +43,7 @@ #define LIBLLDB_LOG_MODULES (1u << 21) #define LIBLLDB_LOG_TARGET (1u << 22) #define LIBLLDB_LOG_MMAP (1u << 23) +#define LIBLLDB_LOG_OS (1u << 24) #define LIBLLDB_LOG_ALL (UINT32_MAX) #define LIBLLDB_LOG_DEFAULT (LIBLLDB_LOG_PROCESS |\ LIBLLDB_LOG_THREAD |\ diff --git a/lldb/include/lldb/lldb-versioning.h b/lldb/include/lldb/lldb-versioning.h new file mode 100644 index 0000000..8ccc67d --- /dev/null +++ b/lldb/include/lldb/lldb-versioning.h @@ -0,0 +1,1607 @@ +//===-- lldb-versioning.h ----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLDB_lldb_versioning_h_ +#define LLDB_lldb_versioning_h_ + +//---------------------------------------------------------------------- +// LLDB API version +//---------------------------------------------------------------------- +#define LLDB_API_MAJOR_VERSION 1 +#define LLDB_API_MINOR_VERSION 0 + +/* + API versioning + --------------------------------- + + The LLDB API is versioned independently of the LLDB source base + Our API version numbers are composed of a major and a minor number + + The major number means a complete and stable revision of the API. Major numbers are compatibility breakers + (i.e. when we change the API major number, there is no promise of compatibility with the previous major version + and we are free to remove and/or change any APIs) + Minor numbers are a work-in-progress evolution of the API. APIs will not be removed or changed across minor versions + (minors do not break compatibility). However, we can deprecate APIs in minor versions or add new APIs in minor versions + A deprecated API is supposedly going to be removed in the next major version and will generate a warning if used + APIs we add in minor versions will not be removed (at least until the following major) but they might theoretically be deprecated + in a following minor version + Users are discouraged from using the LLDB version number to test for API features and should instead use the API version checking + as discussed below + + API version checking + --------------------------------- + + You can (optionally) sign into an API version checking feature + To do so you need to define three macros: + LLDB_API_CHECK_VERSIONING - define to any value (or no value) + LLDB_API_MAJOR_VERSION_WANTED - which major version of the LLDB API you are targeting + LLDB_API_MINOR_VERSION_WANTED - which minor version of the LLDB API you are targeting + + If these macros exist - LLDB will enable version checking of the public API + + If LLDB_API_MAJOR_VERSION is not equal to LLDB_API_MAJOR_VERSION_WANTED we will immediately halt your compilation with an error + This is by design, since we do not make any promise of compatibility across major versions - if you really want to test your luck, disable the versioning altogether + + If the major version test passes, you have signed up for a specific minor version of the API + Whenever we add or deprecate an API in a minor version, we will mark it with either + LLDB_API_NEW_IN_DOT_x - this API is new in LLDB .x + LLDB_API_DEPRECATED_IN_DOT_x - this API is deprecated as of .x + + If you are using an API new in DOT_x + if LLDB_API_MINOR_VERSION_WANTED >= x then all is well, else you will get a compilation error + This is meant to prevent you from using APIs that are newer than whatever LLDB you want to target + + If you are using an API deprecated in DOT_x + if LLDB_API_MINOR_VERSION_WANTED >= x then you will get a compilation warning, else all is well + This is meant to let you know that you are using an API that is deprecated and might go away + + Caveats + --------------------------------- + + Version checking only works on clang on OSX - you will get an error if you try to enable it on any other OS/compiler + If you want to enable version checking on other platforms, you will need to define appropriate implementations for + LLDB_API_IMPL_DEPRECATED and LLDB_API_IMPL_TOONEW and any other infrastructure your compiler needs for this purpose + + We have no deprecation-as-error mode + + There is no support for API versioning in Python + + We reserve to use macros whose names begin with LLDB_API_ and you should not use them in your source code as they might conflict + with present or future macro names we are using to implement versioning +*/ + +// if you want the version checking to work on other OS/compiler, define appropriate IMPL_DEPRECATED/IMPL_TOONEW +// and define LLDB_API_CHECK_VERSIONING_WORKS when you are ready to go live +#if defined(__APPLE__) && defined(__clang__) +#define LLDB_API_IMPL_DEPRECATED __attribute__((deprecated)) +#define LLDB_API_IMPL_TOONEW __attribute__((unavailable)) +#define LLDB_API_CHECK_VERSIONING_WORKS +#endif + +#if defined(LLDB_API_CHECK_VERSIONING) && !defined(LLDB_API_CHECK_VERSIONING_WORKS) +#error "API version checking will not work here - please disable or create and submit patches to lldb-versioning.h" +#endif + +#if defined(LLDB_API_CHECK_VERSIONING_WORKS) && (!defined(LLDB_API_IMPL_DEPRECATED) || !defined(LLDB_API_IMPL_TOONEW)) +#error "LLDB_API_CHECK_VERSIONING_WORKS needs LLDB_API_IMPL_DEPRECATED and LLDB_API_IMPL_TOONEW to be defined" +#endif + +#if defined(LLDB_API_CHECK_VERSIONING) && defined(LLDB_API_MAJOR_VERSION_WANTED) && defined(LLDB_API_MINOR_VERSION_WANTED) + +#if defined (LLDB_API_MAJOR_VERSION) && (LLDB_API_MAJOR_VERSION != LLDB_API_MAJOR_VERSION_WANTED) +#error "Cannot link using this LLDB version - public API versions are incompatible" +#endif + +#define LLDB_API_MINOR_VERSION_DOT_0 0 +#define LLDB_API_MINOR_VERSION_DOT_1 1 +#define LLDB_API_MINOR_VERSION_DOT_2 2 +#define LLDB_API_MINOR_VERSION_DOT_3 3 +#define LLDB_API_MINOR_VERSION_DOT_4 4 +#define LLDB_API_MINOR_VERSION_DOT_5 5 +#define LLDB_API_MINOR_VERSION_DOT_6 6 +#define LLDB_API_MINOR_VERSION_DOT_7 7 +#define LLDB_API_MINOR_VERSION_DOT_8 8 +#define LLDB_API_MINOR_VERSION_DOT_9 9 +#define LLDB_API_MINOR_VERSION_DOT_10 10 +#define LLDB_API_MINOR_VERSION_DOT_11 11 +#define LLDB_API_MINOR_VERSION_DOT_12 12 +#define LLDB_API_MINOR_VERSION_DOT_13 13 +#define LLDB_API_MINOR_VERSION_DOT_14 14 +#define LLDB_API_MINOR_VERSION_DOT_15 15 +#define LLDB_API_MINOR_VERSION_DOT_16 16 +#define LLDB_API_MINOR_VERSION_DOT_17 17 +#define LLDB_API_MINOR_VERSION_DOT_18 18 +#define LLDB_API_MINOR_VERSION_DOT_19 19 +#define LLDB_API_MINOR_VERSION_DOT_20 20 +#define LLDB_API_MINOR_VERSION_DOT_21 21 +#define LLDB_API_MINOR_VERSION_DOT_22 22 +#define LLDB_API_MINOR_VERSION_DOT_23 23 +#define LLDB_API_MINOR_VERSION_DOT_24 24 +#define LLDB_API_MINOR_VERSION_DOT_25 25 +#define LLDB_API_MINOR_VERSION_DOT_26 26 +#define LLDB_API_MINOR_VERSION_DOT_27 27 +#define LLDB_API_MINOR_VERSION_DOT_28 28 +#define LLDB_API_MINOR_VERSION_DOT_29 29 +#define LLDB_API_MINOR_VERSION_DOT_30 30 +#define LLDB_API_MINOR_VERSION_DOT_31 31 +#define LLDB_API_MINOR_VERSION_DOT_32 32 +#define LLDB_API_MINOR_VERSION_DOT_33 33 +#define LLDB_API_MINOR_VERSION_DOT_34 34 +#define LLDB_API_MINOR_VERSION_DOT_35 35 +#define LLDB_API_MINOR_VERSION_DOT_36 36 +#define LLDB_API_MINOR_VERSION_DOT_37 37 +#define LLDB_API_MINOR_VERSION_DOT_38 38 +#define LLDB_API_MINOR_VERSION_DOT_39 39 +#define LLDB_API_MINOR_VERSION_DOT_40 40 +#define LLDB_API_MINOR_VERSION_DOT_41 41 +#define LLDB_API_MINOR_VERSION_DOT_42 42 +#define LLDB_API_MINOR_VERSION_DOT_43 43 +#define LLDB_API_MINOR_VERSION_DOT_44 44 +#define LLDB_API_MINOR_VERSION_DOT_45 45 +#define LLDB_API_MINOR_VERSION_DOT_46 46 +#define LLDB_API_MINOR_VERSION_DOT_47 47 +#define LLDB_API_MINOR_VERSION_DOT_48 48 +#define LLDB_API_MINOR_VERSION_DOT_49 49 +#define LLDB_API_MINOR_VERSION_DOT_50 50 +#define LLDB_API_MINOR_VERSION_DOT_51 51 +#define LLDB_API_MINOR_VERSION_DOT_52 52 +#define LLDB_API_MINOR_VERSION_DOT_53 53 +#define LLDB_API_MINOR_VERSION_DOT_54 54 +#define LLDB_API_MINOR_VERSION_DOT_55 55 +#define LLDB_API_MINOR_VERSION_DOT_56 56 +#define LLDB_API_MINOR_VERSION_DOT_57 57 +#define LLDB_API_MINOR_VERSION_DOT_58 58 +#define LLDB_API_MINOR_VERSION_DOT_59 59 +#define LLDB_API_MINOR_VERSION_DOT_60 60 +#define LLDB_API_MINOR_VERSION_DOT_61 61 +#define LLDB_API_MINOR_VERSION_DOT_62 62 +#define LLDB_API_MINOR_VERSION_DOT_63 63 +#define LLDB_API_MINOR_VERSION_DOT_64 64 +#define LLDB_API_MINOR_VERSION_DOT_65 65 +#define LLDB_API_MINOR_VERSION_DOT_66 66 +#define LLDB_API_MINOR_VERSION_DOT_67 67 +#define LLDB_API_MINOR_VERSION_DOT_68 68 +#define LLDB_API_MINOR_VERSION_DOT_69 69 +#define LLDB_API_MINOR_VERSION_DOT_70 70 +#define LLDB_API_MINOR_VERSION_DOT_71 71 +#define LLDB_API_MINOR_VERSION_DOT_72 72 +#define LLDB_API_MINOR_VERSION_DOT_73 73 +#define LLDB_API_MINOR_VERSION_DOT_74 74 +#define LLDB_API_MINOR_VERSION_DOT_75 75 +#define LLDB_API_MINOR_VERSION_DOT_76 76 +#define LLDB_API_MINOR_VERSION_DOT_77 77 +#define LLDB_API_MINOR_VERSION_DOT_78 78 +#define LLDB_API_MINOR_VERSION_DOT_79 79 +#define LLDB_API_MINOR_VERSION_DOT_80 80 +#define LLDB_API_MINOR_VERSION_DOT_81 81 +#define LLDB_API_MINOR_VERSION_DOT_82 82 +#define LLDB_API_MINOR_VERSION_DOT_83 83 +#define LLDB_API_MINOR_VERSION_DOT_84 84 +#define LLDB_API_MINOR_VERSION_DOT_85 85 +#define LLDB_API_MINOR_VERSION_DOT_86 86 +#define LLDB_API_MINOR_VERSION_DOT_87 87 +#define LLDB_API_MINOR_VERSION_DOT_88 88 +#define LLDB_API_MINOR_VERSION_DOT_89 89 +#define LLDB_API_MINOR_VERSION_DOT_90 90 +#define LLDB_API_MINOR_VERSION_DOT_91 91 +#define LLDB_API_MINOR_VERSION_DOT_92 92 +#define LLDB_API_MINOR_VERSION_DOT_93 93 +#define LLDB_API_MINOR_VERSION_DOT_94 94 +#define LLDB_API_MINOR_VERSION_DOT_95 95 +#define LLDB_API_MINOR_VERSION_DOT_96 96 +#define LLDB_API_MINOR_VERSION_DOT_97 97 +#define LLDB_API_MINOR_VERSION_DOT_98 98 +#define LLDB_API_MINOR_VERSION_DOT_99 99 + +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_0 +#define LLDB_API_NEW_IN_DOT_0 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_0 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_0 +#define LLDB_API_DEPRECATED_IN_DOT_0 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_0 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_1 +#define LLDB_API_NEW_IN_DOT_1 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_1 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_1 +#define LLDB_API_DEPRECATED_IN_DOT_1 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_1 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_2 +#define LLDB_API_NEW_IN_DOT_2 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_2 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_2 +#define LLDB_API_DEPRECATED_IN_DOT_2 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_2 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_3 +#define LLDB_API_NEW_IN_DOT_3 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_3 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_3 +#define LLDB_API_DEPRECATED_IN_DOT_3 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_3 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_4 +#define LLDB_API_NEW_IN_DOT_4 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_4 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_4 +#define LLDB_API_DEPRECATED_IN_DOT_4 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_4 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_5 +#define LLDB_API_NEW_IN_DOT_5 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_5 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_5 +#define LLDB_API_DEPRECATED_IN_DOT_5 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_5 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_6 +#define LLDB_API_NEW_IN_DOT_6 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_6 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_6 +#define LLDB_API_DEPRECATED_IN_DOT_6 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_6 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_7 +#define LLDB_API_NEW_IN_DOT_7 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_7 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_7 +#define LLDB_API_DEPRECATED_IN_DOT_7 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_7 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_8 +#define LLDB_API_NEW_IN_DOT_8 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_8 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_8 +#define LLDB_API_DEPRECATED_IN_DOT_8 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_8 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_9 +#define LLDB_API_NEW_IN_DOT_9 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_9 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_9 +#define LLDB_API_DEPRECATED_IN_DOT_9 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_9 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_10 +#define LLDB_API_NEW_IN_DOT_10 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_10 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_10 +#define LLDB_API_DEPRECATED_IN_DOT_10 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_10 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_11 +#define LLDB_API_NEW_IN_DOT_11 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_11 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_11 +#define LLDB_API_DEPRECATED_IN_DOT_11 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_11 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_12 +#define LLDB_API_NEW_IN_DOT_12 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_12 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_12 +#define LLDB_API_DEPRECATED_IN_DOT_12 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_12 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_13 +#define LLDB_API_NEW_IN_DOT_13 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_13 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_13 +#define LLDB_API_DEPRECATED_IN_DOT_13 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_13 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_14 +#define LLDB_API_NEW_IN_DOT_14 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_14 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_14 +#define LLDB_API_DEPRECATED_IN_DOT_14 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_14 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_15 +#define LLDB_API_NEW_IN_DOT_15 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_15 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_15 +#define LLDB_API_DEPRECATED_IN_DOT_15 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_15 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_16 +#define LLDB_API_NEW_IN_DOT_16 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_16 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_16 +#define LLDB_API_DEPRECATED_IN_DOT_16 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_16 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_17 +#define LLDB_API_NEW_IN_DOT_17 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_17 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_17 +#define LLDB_API_DEPRECATED_IN_DOT_17 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_17 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_18 +#define LLDB_API_NEW_IN_DOT_18 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_18 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_18 +#define LLDB_API_DEPRECATED_IN_DOT_18 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_18 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_19 +#define LLDB_API_NEW_IN_DOT_19 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_19 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_19 +#define LLDB_API_DEPRECATED_IN_DOT_19 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_19 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_20 +#define LLDB_API_NEW_IN_DOT_20 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_20 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_20 +#define LLDB_API_DEPRECATED_IN_DOT_20 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_20 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_21 +#define LLDB_API_NEW_IN_DOT_21 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_21 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_21 +#define LLDB_API_DEPRECATED_IN_DOT_21 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_21 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_22 +#define LLDB_API_NEW_IN_DOT_22 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_22 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_22 +#define LLDB_API_DEPRECATED_IN_DOT_22 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_22 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_23 +#define LLDB_API_NEW_IN_DOT_23 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_23 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_23 +#define LLDB_API_DEPRECATED_IN_DOT_23 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_23 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_24 +#define LLDB_API_NEW_IN_DOT_24 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_24 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_24 +#define LLDB_API_DEPRECATED_IN_DOT_24 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_24 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_25 +#define LLDB_API_NEW_IN_DOT_25 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_25 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_25 +#define LLDB_API_DEPRECATED_IN_DOT_25 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_25 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_26 +#define LLDB_API_NEW_IN_DOT_26 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_26 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_26 +#define LLDB_API_DEPRECATED_IN_DOT_26 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_26 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_27 +#define LLDB_API_NEW_IN_DOT_27 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_27 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_27 +#define LLDB_API_DEPRECATED_IN_DOT_27 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_27 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_28 +#define LLDB_API_NEW_IN_DOT_28 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_28 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_28 +#define LLDB_API_DEPRECATED_IN_DOT_28 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_28 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_29 +#define LLDB_API_NEW_IN_DOT_29 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_29 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_29 +#define LLDB_API_DEPRECATED_IN_DOT_29 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_29 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_30 +#define LLDB_API_NEW_IN_DOT_30 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_30 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_30 +#define LLDB_API_DEPRECATED_IN_DOT_30 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_30 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_31 +#define LLDB_API_NEW_IN_DOT_31 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_31 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_31 +#define LLDB_API_DEPRECATED_IN_DOT_31 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_31 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_32 +#define LLDB_API_NEW_IN_DOT_32 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_32 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_32 +#define LLDB_API_DEPRECATED_IN_DOT_32 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_32 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_33 +#define LLDB_API_NEW_IN_DOT_33 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_33 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_33 +#define LLDB_API_DEPRECATED_IN_DOT_33 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_33 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_34 +#define LLDB_API_NEW_IN_DOT_34 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_34 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_34 +#define LLDB_API_DEPRECATED_IN_DOT_34 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_34 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_35 +#define LLDB_API_NEW_IN_DOT_35 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_35 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_35 +#define LLDB_API_DEPRECATED_IN_DOT_35 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_35 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_36 +#define LLDB_API_NEW_IN_DOT_36 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_36 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_36 +#define LLDB_API_DEPRECATED_IN_DOT_36 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_36 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_37 +#define LLDB_API_NEW_IN_DOT_37 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_37 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_37 +#define LLDB_API_DEPRECATED_IN_DOT_37 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_37 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_38 +#define LLDB_API_NEW_IN_DOT_38 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_38 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_38 +#define LLDB_API_DEPRECATED_IN_DOT_38 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_38 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_39 +#define LLDB_API_NEW_IN_DOT_39 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_39 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_39 +#define LLDB_API_DEPRECATED_IN_DOT_39 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_39 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_40 +#define LLDB_API_NEW_IN_DOT_40 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_40 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_40 +#define LLDB_API_DEPRECATED_IN_DOT_40 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_40 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_41 +#define LLDB_API_NEW_IN_DOT_41 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_41 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_41 +#define LLDB_API_DEPRECATED_IN_DOT_41 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_41 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_42 +#define LLDB_API_NEW_IN_DOT_42 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_42 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_42 +#define LLDB_API_DEPRECATED_IN_DOT_42 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_42 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_43 +#define LLDB_API_NEW_IN_DOT_43 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_43 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_43 +#define LLDB_API_DEPRECATED_IN_DOT_43 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_43 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_44 +#define LLDB_API_NEW_IN_DOT_44 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_44 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_44 +#define LLDB_API_DEPRECATED_IN_DOT_44 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_44 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_45 +#define LLDB_API_NEW_IN_DOT_45 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_45 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_45 +#define LLDB_API_DEPRECATED_IN_DOT_45 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_45 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_46 +#define LLDB_API_NEW_IN_DOT_46 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_46 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_46 +#define LLDB_API_DEPRECATED_IN_DOT_46 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_46 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_47 +#define LLDB_API_NEW_IN_DOT_47 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_47 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_47 +#define LLDB_API_DEPRECATED_IN_DOT_47 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_47 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_48 +#define LLDB_API_NEW_IN_DOT_48 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_48 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_48 +#define LLDB_API_DEPRECATED_IN_DOT_48 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_48 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_49 +#define LLDB_API_NEW_IN_DOT_49 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_49 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_49 +#define LLDB_API_DEPRECATED_IN_DOT_49 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_49 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_50 +#define LLDB_API_NEW_IN_DOT_50 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_50 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_50 +#define LLDB_API_DEPRECATED_IN_DOT_50 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_50 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_51 +#define LLDB_API_NEW_IN_DOT_51 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_51 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_51 +#define LLDB_API_DEPRECATED_IN_DOT_51 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_51 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_52 +#define LLDB_API_NEW_IN_DOT_52 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_52 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_52 +#define LLDB_API_DEPRECATED_IN_DOT_52 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_52 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_53 +#define LLDB_API_NEW_IN_DOT_53 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_53 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_53 +#define LLDB_API_DEPRECATED_IN_DOT_53 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_53 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_54 +#define LLDB_API_NEW_IN_DOT_54 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_54 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_54 +#define LLDB_API_DEPRECATED_IN_DOT_54 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_54 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_55 +#define LLDB_API_NEW_IN_DOT_55 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_55 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_55 +#define LLDB_API_DEPRECATED_IN_DOT_55 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_55 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_56 +#define LLDB_API_NEW_IN_DOT_56 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_56 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_56 +#define LLDB_API_DEPRECATED_IN_DOT_56 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_56 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_57 +#define LLDB_API_NEW_IN_DOT_57 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_57 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_57 +#define LLDB_API_DEPRECATED_IN_DOT_57 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_57 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_58 +#define LLDB_API_NEW_IN_DOT_58 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_58 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_58 +#define LLDB_API_DEPRECATED_IN_DOT_58 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_58 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_59 +#define LLDB_API_NEW_IN_DOT_59 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_59 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_59 +#define LLDB_API_DEPRECATED_IN_DOT_59 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_59 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_60 +#define LLDB_API_NEW_IN_DOT_60 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_60 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_60 +#define LLDB_API_DEPRECATED_IN_DOT_60 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_60 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_61 +#define LLDB_API_NEW_IN_DOT_61 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_61 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_61 +#define LLDB_API_DEPRECATED_IN_DOT_61 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_61 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_62 +#define LLDB_API_NEW_IN_DOT_62 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_62 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_62 +#define LLDB_API_DEPRECATED_IN_DOT_62 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_62 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_63 +#define LLDB_API_NEW_IN_DOT_63 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_63 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_63 +#define LLDB_API_DEPRECATED_IN_DOT_63 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_63 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_64 +#define LLDB_API_NEW_IN_DOT_64 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_64 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_64 +#define LLDB_API_DEPRECATED_IN_DOT_64 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_64 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_65 +#define LLDB_API_NEW_IN_DOT_65 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_65 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_65 +#define LLDB_API_DEPRECATED_IN_DOT_65 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_65 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_66 +#define LLDB_API_NEW_IN_DOT_66 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_66 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_66 +#define LLDB_API_DEPRECATED_IN_DOT_66 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_66 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_67 +#define LLDB_API_NEW_IN_DOT_67 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_67 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_67 +#define LLDB_API_DEPRECATED_IN_DOT_67 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_67 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_68 +#define LLDB_API_NEW_IN_DOT_68 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_68 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_68 +#define LLDB_API_DEPRECATED_IN_DOT_68 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_68 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_69 +#define LLDB_API_NEW_IN_DOT_69 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_69 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_69 +#define LLDB_API_DEPRECATED_IN_DOT_69 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_69 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_70 +#define LLDB_API_NEW_IN_DOT_70 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_70 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_70 +#define LLDB_API_DEPRECATED_IN_DOT_70 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_70 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_71 +#define LLDB_API_NEW_IN_DOT_71 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_71 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_71 +#define LLDB_API_DEPRECATED_IN_DOT_71 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_71 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_72 +#define LLDB_API_NEW_IN_DOT_72 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_72 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_72 +#define LLDB_API_DEPRECATED_IN_DOT_72 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_72 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_73 +#define LLDB_API_NEW_IN_DOT_73 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_73 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_73 +#define LLDB_API_DEPRECATED_IN_DOT_73 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_73 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_74 +#define LLDB_API_NEW_IN_DOT_74 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_74 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_74 +#define LLDB_API_DEPRECATED_IN_DOT_74 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_74 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_75 +#define LLDB_API_NEW_IN_DOT_75 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_75 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_75 +#define LLDB_API_DEPRECATED_IN_DOT_75 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_75 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_76 +#define LLDB_API_NEW_IN_DOT_76 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_76 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_76 +#define LLDB_API_DEPRECATED_IN_DOT_76 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_76 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_77 +#define LLDB_API_NEW_IN_DOT_77 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_77 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_77 +#define LLDB_API_DEPRECATED_IN_DOT_77 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_77 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_78 +#define LLDB_API_NEW_IN_DOT_78 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_78 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_78 +#define LLDB_API_DEPRECATED_IN_DOT_78 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_78 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_79 +#define LLDB_API_NEW_IN_DOT_79 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_79 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_79 +#define LLDB_API_DEPRECATED_IN_DOT_79 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_79 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_80 +#define LLDB_API_NEW_IN_DOT_80 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_80 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_80 +#define LLDB_API_DEPRECATED_IN_DOT_80 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_80 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_81 +#define LLDB_API_NEW_IN_DOT_81 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_81 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_81 +#define LLDB_API_DEPRECATED_IN_DOT_81 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_81 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_82 +#define LLDB_API_NEW_IN_DOT_82 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_82 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_82 +#define LLDB_API_DEPRECATED_IN_DOT_82 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_82 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_83 +#define LLDB_API_NEW_IN_DOT_83 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_83 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_83 +#define LLDB_API_DEPRECATED_IN_DOT_83 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_83 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_84 +#define LLDB_API_NEW_IN_DOT_84 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_84 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_84 +#define LLDB_API_DEPRECATED_IN_DOT_84 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_84 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_85 +#define LLDB_API_NEW_IN_DOT_85 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_85 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_85 +#define LLDB_API_DEPRECATED_IN_DOT_85 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_85 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_86 +#define LLDB_API_NEW_IN_DOT_86 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_86 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_86 +#define LLDB_API_DEPRECATED_IN_DOT_86 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_86 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_87 +#define LLDB_API_NEW_IN_DOT_87 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_87 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_87 +#define LLDB_API_DEPRECATED_IN_DOT_87 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_87 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_88 +#define LLDB_API_NEW_IN_DOT_88 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_88 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_88 +#define LLDB_API_DEPRECATED_IN_DOT_88 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_88 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_89 +#define LLDB_API_NEW_IN_DOT_89 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_89 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_89 +#define LLDB_API_DEPRECATED_IN_DOT_89 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_89 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_90 +#define LLDB_API_NEW_IN_DOT_90 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_90 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_90 +#define LLDB_API_DEPRECATED_IN_DOT_90 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_90 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_91 +#define LLDB_API_NEW_IN_DOT_91 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_91 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_91 +#define LLDB_API_DEPRECATED_IN_DOT_91 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_91 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_92 +#define LLDB_API_NEW_IN_DOT_92 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_92 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_92 +#define LLDB_API_DEPRECATED_IN_DOT_92 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_92 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_93 +#define LLDB_API_NEW_IN_DOT_93 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_93 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_93 +#define LLDB_API_DEPRECATED_IN_DOT_93 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_93 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_94 +#define LLDB_API_NEW_IN_DOT_94 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_94 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_94 +#define LLDB_API_DEPRECATED_IN_DOT_94 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_94 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_95 +#define LLDB_API_NEW_IN_DOT_95 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_95 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_95 +#define LLDB_API_DEPRECATED_IN_DOT_95 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_95 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_96 +#define LLDB_API_NEW_IN_DOT_96 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_96 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_96 +#define LLDB_API_DEPRECATED_IN_DOT_96 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_96 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_97 +#define LLDB_API_NEW_IN_DOT_97 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_97 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_97 +#define LLDB_API_DEPRECATED_IN_DOT_97 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_97 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_98 +#define LLDB_API_NEW_IN_DOT_98 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_98 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_98 +#define LLDB_API_DEPRECATED_IN_DOT_98 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_98 +#endif +#if LLDB_API_MINOR_VERSION_WANTED < LLDB_API_MINOR_VERSION_DOT_99 +#define LLDB_API_NEW_IN_DOT_99 LLDB_API_IMPL_TOONEW +#else +#define LLDB_API_NEW_IN_DOT_99 +#endif + + +#if LLDB_API_MINOR_VERSION_WANTED >= LLDB_API_MINOR_VERSION_DOT_99 +#define LLDB_API_DEPRECATED_IN_DOT_99 LLDB_API_IMPL_DEPRECATED +#else +#define LLDB_API_DEPRECATED_IN_DOT_99 +#endif + +#else // defined(LLDB_CHECK_API_VERSIONING) && defined(LLDB_API_MAJOR_VERSION_WANTED) && defined(LLDB_API_MINOR_VERSION_WANTED) && defined (LLDB_API_MAJOR_VERSION) + +#define LLDB_API_NEW_IN_DOT_0 +#define LLDB_API_DEPRECATED_IN_DOT_0 +#define LLDB_API_NEW_IN_DOT_1 +#define LLDB_API_DEPRECATED_IN_DOT_1 +#define LLDB_API_NEW_IN_DOT_2 +#define LLDB_API_DEPRECATED_IN_DOT_2 +#define LLDB_API_NEW_IN_DOT_3 +#define LLDB_API_DEPRECATED_IN_DOT_3 +#define LLDB_API_NEW_IN_DOT_4 +#define LLDB_API_DEPRECATED_IN_DOT_4 +#define LLDB_API_NEW_IN_DOT_5 +#define LLDB_API_DEPRECATED_IN_DOT_5 +#define LLDB_API_NEW_IN_DOT_6 +#define LLDB_API_DEPRECATED_IN_DOT_6 +#define LLDB_API_NEW_IN_DOT_7 +#define LLDB_API_DEPRECATED_IN_DOT_7 +#define LLDB_API_NEW_IN_DOT_8 +#define LLDB_API_DEPRECATED_IN_DOT_8 +#define LLDB_API_NEW_IN_DOT_9 +#define LLDB_API_DEPRECATED_IN_DOT_9 +#define LLDB_API_NEW_IN_DOT_10 +#define LLDB_API_DEPRECATED_IN_DOT_10 +#define LLDB_API_NEW_IN_DOT_11 +#define LLDB_API_DEPRECATED_IN_DOT_11 +#define LLDB_API_NEW_IN_DOT_12 +#define LLDB_API_DEPRECATED_IN_DOT_12 +#define LLDB_API_NEW_IN_DOT_13 +#define LLDB_API_DEPRECATED_IN_DOT_13 +#define LLDB_API_NEW_IN_DOT_14 +#define LLDB_API_DEPRECATED_IN_DOT_14 +#define LLDB_API_NEW_IN_DOT_15 +#define LLDB_API_DEPRECATED_IN_DOT_15 +#define LLDB_API_NEW_IN_DOT_16 +#define LLDB_API_DEPRECATED_IN_DOT_16 +#define LLDB_API_NEW_IN_DOT_17 +#define LLDB_API_DEPRECATED_IN_DOT_17 +#define LLDB_API_NEW_IN_DOT_18 +#define LLDB_API_DEPRECATED_IN_DOT_18 +#define LLDB_API_NEW_IN_DOT_19 +#define LLDB_API_DEPRECATED_IN_DOT_19 +#define LLDB_API_NEW_IN_DOT_20 +#define LLDB_API_DEPRECATED_IN_DOT_20 +#define LLDB_API_NEW_IN_DOT_21 +#define LLDB_API_DEPRECATED_IN_DOT_21 +#define LLDB_API_NEW_IN_DOT_22 +#define LLDB_API_DEPRECATED_IN_DOT_22 +#define LLDB_API_NEW_IN_DOT_23 +#define LLDB_API_DEPRECATED_IN_DOT_23 +#define LLDB_API_NEW_IN_DOT_24 +#define LLDB_API_DEPRECATED_IN_DOT_24 +#define LLDB_API_NEW_IN_DOT_25 +#define LLDB_API_DEPRECATED_IN_DOT_25 +#define LLDB_API_NEW_IN_DOT_26 +#define LLDB_API_DEPRECATED_IN_DOT_26 +#define LLDB_API_NEW_IN_DOT_27 +#define LLDB_API_DEPRECATED_IN_DOT_27 +#define LLDB_API_NEW_IN_DOT_28 +#define LLDB_API_DEPRECATED_IN_DOT_28 +#define LLDB_API_NEW_IN_DOT_29 +#define LLDB_API_DEPRECATED_IN_DOT_29 +#define LLDB_API_NEW_IN_DOT_30 +#define LLDB_API_DEPRECATED_IN_DOT_30 +#define LLDB_API_NEW_IN_DOT_31 +#define LLDB_API_DEPRECATED_IN_DOT_31 +#define LLDB_API_NEW_IN_DOT_32 +#define LLDB_API_DEPRECATED_IN_DOT_32 +#define LLDB_API_NEW_IN_DOT_33 +#define LLDB_API_DEPRECATED_IN_DOT_33 +#define LLDB_API_NEW_IN_DOT_34 +#define LLDB_API_DEPRECATED_IN_DOT_34 +#define LLDB_API_NEW_IN_DOT_35 +#define LLDB_API_DEPRECATED_IN_DOT_35 +#define LLDB_API_NEW_IN_DOT_36 +#define LLDB_API_DEPRECATED_IN_DOT_36 +#define LLDB_API_NEW_IN_DOT_37 +#define LLDB_API_DEPRECATED_IN_DOT_37 +#define LLDB_API_NEW_IN_DOT_38 +#define LLDB_API_DEPRECATED_IN_DOT_38 +#define LLDB_API_NEW_IN_DOT_39 +#define LLDB_API_DEPRECATED_IN_DOT_39 +#define LLDB_API_NEW_IN_DOT_40 +#define LLDB_API_DEPRECATED_IN_DOT_40 +#define LLDB_API_NEW_IN_DOT_41 +#define LLDB_API_DEPRECATED_IN_DOT_41 +#define LLDB_API_NEW_IN_DOT_42 +#define LLDB_API_DEPRECATED_IN_DOT_42 +#define LLDB_API_NEW_IN_DOT_43 +#define LLDB_API_DEPRECATED_IN_DOT_43 +#define LLDB_API_NEW_IN_DOT_44 +#define LLDB_API_DEPRECATED_IN_DOT_44 +#define LLDB_API_NEW_IN_DOT_45 +#define LLDB_API_DEPRECATED_IN_DOT_45 +#define LLDB_API_NEW_IN_DOT_46 +#define LLDB_API_DEPRECATED_IN_DOT_46 +#define LLDB_API_NEW_IN_DOT_47 +#define LLDB_API_DEPRECATED_IN_DOT_47 +#define LLDB_API_NEW_IN_DOT_48 +#define LLDB_API_DEPRECATED_IN_DOT_48 +#define LLDB_API_NEW_IN_DOT_49 +#define LLDB_API_DEPRECATED_IN_DOT_49 +#define LLDB_API_NEW_IN_DOT_50 +#define LLDB_API_DEPRECATED_IN_DOT_50 +#define LLDB_API_NEW_IN_DOT_51 +#define LLDB_API_DEPRECATED_IN_DOT_51 +#define LLDB_API_NEW_IN_DOT_52 +#define LLDB_API_DEPRECATED_IN_DOT_52 +#define LLDB_API_NEW_IN_DOT_53 +#define LLDB_API_DEPRECATED_IN_DOT_53 +#define LLDB_API_NEW_IN_DOT_54 +#define LLDB_API_DEPRECATED_IN_DOT_54 +#define LLDB_API_NEW_IN_DOT_55 +#define LLDB_API_DEPRECATED_IN_DOT_55 +#define LLDB_API_NEW_IN_DOT_56 +#define LLDB_API_DEPRECATED_IN_DOT_56 +#define LLDB_API_NEW_IN_DOT_57 +#define LLDB_API_DEPRECATED_IN_DOT_57 +#define LLDB_API_NEW_IN_DOT_58 +#define LLDB_API_DEPRECATED_IN_DOT_58 +#define LLDB_API_NEW_IN_DOT_59 +#define LLDB_API_DEPRECATED_IN_DOT_59 +#define LLDB_API_NEW_IN_DOT_60 +#define LLDB_API_DEPRECATED_IN_DOT_60 +#define LLDB_API_NEW_IN_DOT_61 +#define LLDB_API_DEPRECATED_IN_DOT_61 +#define LLDB_API_NEW_IN_DOT_62 +#define LLDB_API_DEPRECATED_IN_DOT_62 +#define LLDB_API_NEW_IN_DOT_63 +#define LLDB_API_DEPRECATED_IN_DOT_63 +#define LLDB_API_NEW_IN_DOT_64 +#define LLDB_API_DEPRECATED_IN_DOT_64 +#define LLDB_API_NEW_IN_DOT_65 +#define LLDB_API_DEPRECATED_IN_DOT_65 +#define LLDB_API_NEW_IN_DOT_66 +#define LLDB_API_DEPRECATED_IN_DOT_66 +#define LLDB_API_NEW_IN_DOT_67 +#define LLDB_API_DEPRECATED_IN_DOT_67 +#define LLDB_API_NEW_IN_DOT_68 +#define LLDB_API_DEPRECATED_IN_DOT_68 +#define LLDB_API_NEW_IN_DOT_69 +#define LLDB_API_DEPRECATED_IN_DOT_69 +#define LLDB_API_NEW_IN_DOT_70 +#define LLDB_API_DEPRECATED_IN_DOT_70 +#define LLDB_API_NEW_IN_DOT_71 +#define LLDB_API_DEPRECATED_IN_DOT_71 +#define LLDB_API_NEW_IN_DOT_72 +#define LLDB_API_DEPRECATED_IN_DOT_72 +#define LLDB_API_NEW_IN_DOT_73 +#define LLDB_API_DEPRECATED_IN_DOT_73 +#define LLDB_API_NEW_IN_DOT_74 +#define LLDB_API_DEPRECATED_IN_DOT_74 +#define LLDB_API_NEW_IN_DOT_75 +#define LLDB_API_DEPRECATED_IN_DOT_75 +#define LLDB_API_NEW_IN_DOT_76 +#define LLDB_API_DEPRECATED_IN_DOT_76 +#define LLDB_API_NEW_IN_DOT_77 +#define LLDB_API_DEPRECATED_IN_DOT_77 +#define LLDB_API_NEW_IN_DOT_78 +#define LLDB_API_DEPRECATED_IN_DOT_78 +#define LLDB_API_NEW_IN_DOT_79 +#define LLDB_API_DEPRECATED_IN_DOT_79 +#define LLDB_API_NEW_IN_DOT_80 +#define LLDB_API_DEPRECATED_IN_DOT_80 +#define LLDB_API_NEW_IN_DOT_81 +#define LLDB_API_DEPRECATED_IN_DOT_81 +#define LLDB_API_NEW_IN_DOT_82 +#define LLDB_API_DEPRECATED_IN_DOT_82 +#define LLDB_API_NEW_IN_DOT_83 +#define LLDB_API_DEPRECATED_IN_DOT_83 +#define LLDB_API_NEW_IN_DOT_84 +#define LLDB_API_DEPRECATED_IN_DOT_84 +#define LLDB_API_NEW_IN_DOT_85 +#define LLDB_API_DEPRECATED_IN_DOT_85 +#define LLDB_API_NEW_IN_DOT_86 +#define LLDB_API_DEPRECATED_IN_DOT_86 +#define LLDB_API_NEW_IN_DOT_87 +#define LLDB_API_DEPRECATED_IN_DOT_87 +#define LLDB_API_NEW_IN_DOT_88 +#define LLDB_API_DEPRECATED_IN_DOT_88 +#define LLDB_API_NEW_IN_DOT_89 +#define LLDB_API_DEPRECATED_IN_DOT_89 +#define LLDB_API_NEW_IN_DOT_90 +#define LLDB_API_DEPRECATED_IN_DOT_90 +#define LLDB_API_NEW_IN_DOT_91 +#define LLDB_API_DEPRECATED_IN_DOT_91 +#define LLDB_API_NEW_IN_DOT_92 +#define LLDB_API_DEPRECATED_IN_DOT_92 +#define LLDB_API_NEW_IN_DOT_93 +#define LLDB_API_DEPRECATED_IN_DOT_93 +#define LLDB_API_NEW_IN_DOT_94 +#define LLDB_API_DEPRECATED_IN_DOT_94 +#define LLDB_API_NEW_IN_DOT_95 +#define LLDB_API_DEPRECATED_IN_DOT_95 +#define LLDB_API_NEW_IN_DOT_96 +#define LLDB_API_DEPRECATED_IN_DOT_96 +#define LLDB_API_NEW_IN_DOT_97 +#define LLDB_API_DEPRECATED_IN_DOT_97 +#define LLDB_API_NEW_IN_DOT_98 +#define LLDB_API_DEPRECATED_IN_DOT_98 +#define LLDB_API_NEW_IN_DOT_99 +#define LLDB_API_DEPRECATED_IN_DOT_99 +#endif // defined(LLDB_CHECK_API_VERSIONING) && defined(LLDB_API_MAJOR_VERSION_WANTED) && defined(LLDB_API_MINOR_VERSION_WANTED) && defined (LLDB_API_MAJOR_VERSION) + +#endif // LLDB_lldb_versioning_h_
\ No newline at end of file diff --git a/lldb/lib/Makefile b/lldb/lib/Makefile index 7e76b52..e6f2182 100644 --- a/lldb/lib/Makefile +++ b/lldb/lib/Makefile @@ -71,7 +71,7 @@ USEDLIBS = lldbAPI.a \ # Because GCC requires RTTI enabled for lldbCore (see source/Core/Makefile) it is # necessary to also link the clang rewriter libraries so vtable references can # be resolved correctly, if we are building with GCC. -ifeq (g++,$(shell basename $(CXX))) +ifeq (g++,$(shell basename $(CXX) | colrm 4)) USEDLIBS += clangRewriteCore.a \ clangRewriteFrontend.a endif diff --git a/lldb/lldb.xcodeproj/project.pbxproj b/lldb/lldb.xcodeproj/project.pbxproj index ff1eb7c..eeb6b85 100644 --- a/lldb/lldb.xcodeproj/project.pbxproj +++ b/lldb/lldb.xcodeproj/project.pbxproj @@ -519,6 +519,7 @@ 4CF52AF8142829390051E832 /* SBFileSpecList.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4CF52AF7142829390051E832 /* SBFileSpecList.cpp */; }; 94031A9E13CF486700DCFF3C /* InputReaderEZ.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94031A9D13CF486600DCFF3C /* InputReaderEZ.cpp */; }; 94094C6B163B6F840083A547 /* ValueObjectCast.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94094C69163B6CD90083A547 /* ValueObjectCast.cpp */; }; + 94145431175E63B500284436 /* lldb-versioning.h in Headers */ = {isa = PBXBuildFile; fileRef = 94145430175D7FDE00284436 /* lldb-versioning.h */; settings = {ATTRIBUTES = (Public, ); }; }; 941BCC7F14E48C4000BB969C /* SBTypeFilter.h in Headers */ = {isa = PBXBuildFile; fileRef = 9461568614E355F2003A195C /* SBTypeFilter.h */; settings = {ATTRIBUTES = (Public, ); }; }; 941BCC8014E48C4000BB969C /* SBTypeFormat.h in Headers */ = {isa = PBXBuildFile; fileRef = 9461568714E355F2003A195C /* SBTypeFormat.h */; settings = {ATTRIBUTES = (Public, ); }; }; 941BCC8114E48C4000BB969C /* SBTypeSummary.h in Headers */ = {isa = PBXBuildFile; fileRef = 9461568814E355F2003A195C /* SBTypeSummary.h */; settings = {ATTRIBUTES = (Public, ); }; }; @@ -1560,6 +1561,7 @@ 940DB8E116EA674000D3C2F1 /* Xcode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Xcode.cpp; sourceTree = "<group>"; }; 940DB8E216EA674000D3C2F1 /* Xcode.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Xcode.h; sourceTree = "<group>"; }; 940DB8E616EA709400D3C2F1 /* main.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = "<group>"; }; + 94145430175D7FDE00284436 /* lldb-versioning.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "lldb-versioning.h"; path = "include/lldb/lldb-versioning.h"; sourceTree = "<group>"; }; 944372DA171F6B4300E57C32 /* RegisterContextDummy.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = RegisterContextDummy.cpp; path = Utility/RegisterContextDummy.cpp; sourceTree = "<group>"; }; 944372DB171F6B4300E57C32 /* RegisterContextDummy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = RegisterContextDummy.h; path = Utility/RegisterContextDummy.h; sourceTree = "<group>"; }; 9443B120140C18A90013457C /* SBData.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = SBData.h; path = include/lldb/API/SBData.h; sourceTree = "<group>"; }; @@ -2160,6 +2162,7 @@ 26DE1E6A11616C2E00A093E2 /* lldb-forward.h */, 26651A14133BEC76005B64B7 /* lldb-public.h */, 26BC7C2910F1B3BC00F91463 /* lldb-types.h */, + 94145430175D7FDE00284436 /* lldb-versioning.h */, 26B42C4C1187ABA50079C8C8 /* LLDB.h */, 9A9830FC1125FC5800A56CB0 /* SBDefines.h */, 26DE204211618ACA00A093E2 /* SBAddress.h */, @@ -3505,6 +3508,7 @@ 2668020F115FD12C008E1FE4 /* lldb-enumerations.h in Headers */, 26DE1E6C11616C2E00A093E2 /* lldb-forward.h in Headers */, 26680214115FD12C008E1FE4 /* lldb-types.h in Headers */, + 94145431175E63B500284436 /* lldb-versioning.h in Headers */, 26B42C4D1187ABA50079C8C8 /* LLDB.h in Headers */, 26DE204311618ACA00A093E2 /* SBAddress.h in Headers */, 26DE205711618FC500A093E2 /* SBBlock.h in Headers */, diff --git a/lldb/lldb.xcodeproj/xcshareddata/xcschemes/lldb-tool.xcscheme b/lldb/lldb.xcodeproj/xcshareddata/xcschemes/lldb-tool.xcscheme index 34560fe..bd7c34c 100644 --- a/lldb/lldb.xcodeproj/xcshareddata/xcschemes/lldb-tool.xcscheme +++ b/lldb/lldb.xcodeproj/xcshareddata/xcschemes/lldb-tool.xcscheme @@ -84,7 +84,7 @@ <LaunchAction selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB" selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB" - launchStyle = "0" + launchStyle = "1" useCustomWorkingDirectory = "NO" customWorkingDirectory = "/Volumes/work/gclayton/Documents/devb/attach" buildConfiguration = "Debug" diff --git a/lldb/scripts/CMakeLists.txt b/lldb/scripts/CMakeLists.txt index 48c115c..6b60ed69 100644 --- a/lldb/scripts/CMakeLists.txt +++ b/lldb/scripts/CMakeLists.txt @@ -13,3 +13,8 @@ set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/LLDBWrapPython.cpp PROPE ADD_CUSTOM_TARGET(swig_wrapper ALL echo -n
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/LLDBWrapPython.cpp
)
+
+# Install the LLDB python module on all operating systems (except Windows)
+if (NOT CMAKE_SYSTEM_NAME MATCHES "Windows")
+ install(SCRIPT lldb_python_module.cmake -DCMAKE_INSTALL_PREFIX=\"${CMAKE_INSTALL_PREFIX}\" -DCMAKE_BUILD_DIR=\"${CMAKE_BUILD_DIR}\")
+endif()
diff --git a/lldb/scripts/Python/finish-swig-Python-LLDB.sh b/lldb/scripts/Python/finish-swig-Python-LLDB.sh index d057ba9..6f0bc38 100755 --- a/lldb/scripts/Python/finish-swig-Python-LLDB.sh +++ b/lldb/scripts/Python/finish-swig-Python-LLDB.sh @@ -248,7 +248,8 @@ then create_python_package "/macosx" "${package_files}" # lldb/diagnose - package_files="${SRC_ROOT}/examples/python/diagnose_unwind.py" + package_files="${SRC_ROOT}/examples/python/diagnose_unwind.py + ${SRC_ROOT}/examples/python/diagnose_nsstring.py" create_python_package "/diagnose" "${package_files}" # Copy files needed by lldb/macosx/heap.py to build libheap.dylib diff --git a/lldb/scripts/Python/python-wrapper.swig b/lldb/scripts/Python/python-wrapper.swig index 789cf6f..4723a9c 100644 --- a/lldb/scripts/Python/python-wrapper.swig +++ b/lldb/scripts/Python/python-wrapper.swig @@ -318,8 +318,18 @@ LLDBSwigPythonCallTypeScript pvalue = PyObject_CallObject (pfunc, pargs); Py_DECREF (pargs); - if (pvalue != NULL && pvalue != Py_None && PyString_Check(pvalue)) - retval.assign(PyString_AsString(pvalue)); + if (pvalue != NULL && pvalue != Py_None) + { + if (PyString_Check(pvalue)) + retval.assign(PyString_AsString(pvalue)); + else + { + PyObject* value_as_string = PyObject_Str(pvalue); + if (value_as_string && value_as_string != Py_None && PyString_Check(value_as_string)) + retval.assign(PyString_AsString(value_as_string)); + Py_XDECREF(value_as_string); + } + } Py_XDECREF (pvalue); Py_INCREF (session_dict); } diff --git a/lldb/scripts/lldb_python_module.cmake b/lldb/scripts/lldb_python_module.cmake new file mode 100644 index 0000000..5eaf1ad --- /dev/null +++ b/lldb/scripts/lldb_python_module.cmake @@ -0,0 +1,14 @@ +# This CMake script installs the LLDB python module from the build directory +# to the install directory. + +# FIXME: if a non-standard version of python is requested, the cmake macro +# below will need Python_ADDITIONAL_VERSIONS set in order to find it. +include(FindPythonInterp) + +SET(PYTHON_DIRECTORY python${PYTHON_VERSION_MAJOR}.${PYTHON_VERSION_MINOR}) + +SET(lldb_module_src ${CMAKE_CURRENT_BINARY_DIR}/lib/${PYTHON_DIRECTORY}) +SET(lldb_module_dest ${CMAKE_INSTALL_PREFIX}/lib) + +MESSAGE(STATUS "Installing LLDB python module from: ${lldb_module_src} to ${lldb_module_dest}") +FILE(COPY "${lldb_module_src}" DESTINATION "${lldb_module_dest}") diff --git a/lldb/source/API/SBDebugger.cpp b/lldb/source/API/SBDebugger.cpp index bcd9e8f..f5e71d5 100644 --- a/lldb/source/API/SBDebugger.cpp +++ b/lldb/source/API/SBDebugger.cpp @@ -1057,6 +1057,22 @@ SBDebugger::GetUseExternalEditor () } bool +SBDebugger::SetUseColor (bool value) +{ + if (m_opaque_sp) + return m_opaque_sp->SetUseColor (value); + return false; +} + +bool +SBDebugger::GetUseColor () const +{ + if (m_opaque_sp) + return m_opaque_sp->GetUseColor (); + return false; +} + +bool SBDebugger::GetDescription (SBStream &description) { Stream &strm = description.ref(); diff --git a/lldb/source/Breakpoint/BreakpointLocation.cpp b/lldb/source/Breakpoint/BreakpointLocation.cpp index fe8267d..ba8f1de 100644 --- a/lldb/source/Breakpoint/BreakpointLocation.cpp +++ b/lldb/source/Breakpoint/BreakpointLocation.cpp @@ -46,7 +46,8 @@ BreakpointLocation::BreakpointLocation m_address (addr), m_owner (owner), m_options_ap (), - m_bp_site_sp () + m_bp_site_sp (), + m_condition_mutex () { SetThreadID (tid); m_being_created = false; @@ -249,6 +250,8 @@ bool BreakpointLocation::ConditionSaysStop (ExecutionContext &exe_ctx, Error &error) { Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_BREAKPOINTS); + + Mutex::Locker evaluation_locker(m_condition_mutex); size_t condition_hash; const char *condition_text = GetConditionText(&condition_hash); @@ -312,6 +315,12 @@ BreakpointLocation::ConditionSaysStop (ExecutionContext &exe_ctx, Error &error) if (result_code == eExecutionCompleted) { + if (!result_variable_sp) + { + ret = false; + error.SetErrorString("Expression did not return a result"); + } + result_value_sp = result_variable_sp->GetValueObject(); if (result_value_sp) diff --git a/lldb/source/CMakeLists.txt b/lldb/source/CMakeLists.txt index 10bf48a..595acf0 100644 --- a/lldb/source/CMakeLists.txt +++ b/lldb/source/CMakeLists.txt @@ -7,6 +7,13 @@ include_directories( )
endif ()
+if ( CMAKE_SYSTEM_NAME MATCHES "FreeBSD" )
+include_directories(
+ Plugins/Process/FreeBSD
+ Plugins/Process/POSIX
+ )
+endif ()
+
add_subdirectory(API)
add_subdirectory(Breakpoint)
add_subdirectory(Commands)
@@ -85,12 +92,27 @@ if ( CMAKE_SYSTEM_NAME MATCHES "Linux" ) )
endif ()
+# FreeBSD-only libraries
+if ( CMAKE_SYSTEM_NAME MATCHES "FreeBSD" )
+ list(APPEND LLDB_USED_LIBS
+ lldbHostFreeBSD
+ lldbPluginProcessFreeBSD
+ lldbPluginProcessPOSIX
+ )
+endif ()
+
# Darwin-only libraries
if ( CMAKE_SYSTEM_NAME MATCHES "Darwin" )
+ set(LLDB_VERS_GENERATED_FILE ${LLDB_BINARY_DIR}/source/LLDB_vers.c)
+ add_custom_command(OUTPUT ${LLDB_VERS_GENERATED_FILE}
+ COMMAND ${LLDB_SOURCE_DIR}/scripts/generate-vers.pl
+ ${LLDB_SOURCE_DIR}/lldb.xcodeproj/project.pbxproj
+ > ${LLDB_VERS_GENERATED_FILE})
+
+ set_source_files_properties(${LLDB_VERS_GENERATED_FILE} PROPERTIES GENERATED 1)
list(APPEND LLDB_USED_LIBS
lldbHostMacOSX
lldbPluginDynamicLoaderDarwinKernel
- lldbPluginOSDarwinKernel
lldbPluginProcessMacOSXKernel
lldbPluginSymbolVendorMacOSX
)
@@ -143,9 +165,17 @@ add_lldb_library(liblldb lldb.cpp
lldb-log.cpp
${LLDB_BINARY_DIR}/scripts/LLDBWrapPython.cpp
+ ${LLDB_VERS_GENERATED_FILE}
+ )
+set_target_properties(liblldb
+ PROPERTIES
+ OUTPUT_NAME lldb
+ VERSION ${LLDB_VERSION}
+ )
+add_dependencies(liblldb
+ ${LLDB_BINARY_DIR}/scripts/LLDBWrapPython.cpp
+ ${LLDB_VERS_GENERATED_FILE}
)
-set_target_properties(liblldb PROPERTIES OUTPUT_NAME lldb)
-add_dependencies(liblldb ${LLDB_BINARY_DIR}/scripts/LLDBWrapPython.cpp)
target_link_libraries(liblldb ${LLDB_SYSTEM_LIBS})
# Determine LLDB revision and repository. GetSourceVersion and GetRepositoryPath are shell-scripts, and as
diff --git a/lldb/source/Commands/CommandObjectApropos.cpp b/lldb/source/Commands/CommandObjectApropos.cpp index 2eeec78..02dc726 100644 --- a/lldb/source/Commands/CommandObjectApropos.cpp +++ b/lldb/source/Commands/CommandObjectApropos.cpp @@ -68,29 +68,59 @@ CommandObjectApropos::DoExecute (Args& args, CommandReturnObject &result) // is private. StringList commands_found; StringList commands_help; - m_interpreter.FindCommandsForApropos (search_word, commands_found, commands_help); - if (commands_found.GetSize() == 0) + StringList user_commands_found; + StringList user_commands_help; + + m_interpreter.FindCommandsForApropos (search_word, commands_found, commands_help, true, false); + m_interpreter.FindCommandsForApropos (search_word, user_commands_found, user_commands_help, false, true); + + if (commands_found.GetSize() == 0 && user_commands_found.GetSize() == 0) { result.AppendMessageWithFormat ("No commands found pertaining to '%s'. Try 'help' to see a complete list of debugger commands.\n", search_word); } else { - result.AppendMessageWithFormat ("The following commands may relate to '%s':\n", search_word); - size_t max_len = 0; - - for (size_t i = 0; i < commands_found.GetSize(); ++i) + if (commands_found.GetSize() > 0) { - size_t len = strlen (commands_found.GetStringAtIndex (i)); - if (len > max_len) - max_len = len; + result.AppendMessageWithFormat ("The following built-in commands may relate to '%s':\n", search_word); + size_t max_len = 0; + + for (size_t i = 0; i < commands_found.GetSize(); ++i) + { + size_t len = strlen (commands_found.GetStringAtIndex (i)); + if (len > max_len) + max_len = len; + } + + for (size_t i = 0; i < commands_found.GetSize(); ++i) + m_interpreter.OutputFormattedHelpText (result.GetOutputStream(), + commands_found.GetStringAtIndex(i), + "--", + commands_help.GetStringAtIndex(i), + max_len); + if (user_commands_found.GetSize() > 0) + result.AppendMessage(""); + } + + if (user_commands_found.GetSize() > 0) + { + result.AppendMessageWithFormat ("The following user commands may relate to '%s':\n", search_word); + size_t max_len = 0; + + for (size_t i = 0; i < user_commands_found.GetSize(); ++i) + { + size_t len = strlen (user_commands_found.GetStringAtIndex (i)); + if (len > max_len) + max_len = len; + } + + for (size_t i = 0; i < user_commands_found.GetSize(); ++i) + m_interpreter.OutputFormattedHelpText (result.GetOutputStream(), + user_commands_found.GetStringAtIndex(i), + "--", + user_commands_help.GetStringAtIndex(i), + max_len); } - - for (size_t i = 0; i < commands_found.GetSize(); ++i) - m_interpreter.OutputFormattedHelpText (result.GetOutputStream(), - commands_found.GetStringAtIndex(i), - "--", - commands_help.GetStringAtIndex(i), - max_len); } diff --git a/lldb/source/Commands/CommandObjectBreakpointCommand.cpp b/lldb/source/Commands/CommandObjectBreakpointCommand.cpp index 3dbb6e0..c4504a4 100644 --- a/lldb/source/Commands/CommandObjectBreakpointCommand.cpp +++ b/lldb/source/Commands/CommandObjectBreakpointCommand.cpp @@ -167,6 +167,12 @@ initialized:\n\ (lldb)\n\ \n\ \n\ +Your Python code, however organized, can optionally return a value.\n\ +If the returned value is False, that tells LLDB not to stop at the breakpoint\n\ +to which the code is associated. Returning anything other than False, or even\n\ +returning None, or even omitting a return statement entirely, will cause\n\ +LLDB to stop.\n\ +\n\ Final Note: If you get a warning that no breakpoint command was generated, but\n\ you did not get any syntax errors, you probably forgot to add a call to your\n\ functions.\n\ diff --git a/lldb/source/Commands/CommandObjectCommands.cpp b/lldb/source/Commands/CommandObjectCommands.cpp index 278fdc0..5964eac 100644 --- a/lldb/source/Commands/CommandObjectCommands.cpp +++ b/lldb/source/Commands/CommandObjectCommands.cpp @@ -1355,7 +1355,7 @@ protected: void OptionParsingStarting () { - m_allow_reload = false; + m_allow_reload = true; } const OptionDefinition* @@ -1426,7 +1426,7 @@ protected: OptionDefinition CommandObjectCommandsScriptImport::CommandOptions::g_option_table[] = { - { LLDB_OPT_SET_1, false, "allow-reload", 'r', no_argument, NULL, 0, eArgTypeNone, "Allow the script to be loaded even if it was already loaded before (for Python, the __lldb_init_module function will be called again, but the module will not be reloaded from disk)."}, + { LLDB_OPT_SET_1, false, "allow-reload", 'r', no_argument, NULL, 0, eArgTypeNone, "Allow the script to be loaded even if it was already loaded before. This argument exists for backwards compatibility, but reloading is always allowed, whether you specify it or not."}, { 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL } }; diff --git a/lldb/source/Commands/CommandObjectExpression.cpp b/lldb/source/Commands/CommandObjectExpression.cpp index c47d477..1d31c67 100644 --- a/lldb/source/Commands/CommandObjectExpression.cpp +++ b/lldb/source/Commands/CommandObjectExpression.cpp @@ -54,7 +54,7 @@ CommandObjectExpression::CommandOptions::g_option_table[] = { { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "all-threads", 'a', required_argument, NULL, 0, eArgTypeBoolean, "Should we run all threads if the execution doesn't complete on one thread."}, { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "ignore-breakpoints", 'i', required_argument, NULL, 0, eArgTypeBoolean, "Ignore breakpoint hits while running expressions"}, - { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "timeout", 't', required_argument, NULL, 0, eArgTypeUnsignedInteger, "Timeout value for running the expression."}, + { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "timeout", 't', required_argument, NULL, 0, eArgTypeUnsignedInteger, "Timeout value (in microseconds) for running the expression."}, { LLDB_OPT_SET_1 | LLDB_OPT_SET_2, false, "unwind-on-error", 'u', required_argument, NULL, 0, eArgTypeBoolean, "Clean up program state if the expression causes a crash, or raises a signal. Note, unlike gdb hitting a breakpoint is controlled by another option (-i)."}, }; diff --git a/lldb/source/Commands/CommandObjectMemory.cpp b/lldb/source/Commands/CommandObjectMemory.cpp index 2859cd1..fe44ad0 100644 --- a/lldb/source/Commands/CommandObjectMemory.cpp +++ b/lldb/source/Commands/CommandObjectMemory.cpp @@ -44,7 +44,7 @@ g_option_table[] = { LLDB_OPT_SET_3, true , "type" ,'t', required_argument, NULL, 0, eArgTypeNone ,"The name of a type to view memory as."}, { LLDB_OPT_SET_1| LLDB_OPT_SET_2| - LLDB_OPT_SET_3, false, "force" ,'r', no_argument, NULL, 0, eArgTypeNone ,"Necessary if reading over 1024 bytes of memory."}, + LLDB_OPT_SET_3, false, "force" ,'r', no_argument, NULL, 0, eArgTypeNone ,"Necessary if reading over target.max-memory-read-size bytes."}, }; @@ -119,6 +119,7 @@ public: m_num_per_line.Clear(); m_output_as_binary = false; m_view_as_type.Clear(); + m_force = false; } Error @@ -642,15 +643,16 @@ protected: item_count = total_byte_size / item_byte_size; } - if (total_byte_size > 1024 && !m_memory_options.m_force) + uint32_t max_unforced_size = target->GetMaximumMemReadSize(); + + if (total_byte_size > max_unforced_size && !m_memory_options.m_force) { - result.AppendErrorWithFormat("Normally, \'memory read\' will not read over 1Kbyte of data.\n"); - result.AppendErrorWithFormat("Please use --force to override this restriction.\n"); + result.AppendErrorWithFormat("Normally, \'memory read\' will not read over %" PRIu32 " bytes of data.\n",max_unforced_size); + result.AppendErrorWithFormat("Please use --force to override this restriction just once.\n"); + result.AppendErrorWithFormat("or set target.max-memory-read-size if you will often need a larger limit.\n"); return false; } - - DataBufferSP data_sp; size_t bytes_read = 0; if (clang_ast_type.GetOpaqueQualType()) @@ -827,12 +829,25 @@ protected: DataExtractor data (data_sp, target->GetArchitecture().GetByteOrder(), target->GetArchitecture().GetAddressByteSize()); - + + Format format = m_format_options.GetFormat(); + if ( ( (format == eFormatChar) || (format == eFormatCharPrintable) ) + && (item_byte_size != 1) + && (item_count == 1)) + { + // this turns requests such as + // memory read -fc -s10 -c1 *charPtrPtr + // which make no sense (what is a char of size 10?) + // into a request for fetching 10 chars of size 1 from the same memory location + format = eFormatCharArray; + item_count = item_byte_size; + item_byte_size = 1; + } assert (output_stream); size_t bytes_dumped = data.Dump (output_stream, 0, - m_format_options.GetFormat(), + format, item_byte_size, item_count, num_per_line, diff --git a/lldb/source/Commands/CommandObjectSettings.cpp b/lldb/source/Commands/CommandObjectSettings.cpp index cafb055..95cc9b6 100644 --- a/lldb/source/Commands/CommandObjectSettings.cpp +++ b/lldb/source/Commands/CommandObjectSettings.cpp @@ -255,7 +255,14 @@ protected: if (error.Success()) { - error = m_interpreter.GetDebugger().SetPropertyValue (&m_exe_ctx, + // FIXME this is the same issue as the one in commands script import + // we could be setting target.load-script-from-symbol-file which would cause + // Python scripts to be loaded, which could run LLDB commands + // (e.g. settings set target.process.python-os-plugin-path) and cause a crash + // if we did not clear the command's exe_ctx first + ExecutionContext exe_ctx(m_exe_ctx); + m_exe_ctx.Clear(); + error = m_interpreter.GetDebugger().SetPropertyValue (&exe_ctx, eVarSetOperationAssign, var_name, var_value_cstr); diff --git a/lldb/source/Commands/CommandObjectSource.cpp b/lldb/source/Commands/CommandObjectSource.cpp index 4553867..df20ea1 100644 --- a/lldb/source/Commands/CommandObjectSource.cpp +++ b/lldb/source/Commands/CommandObjectSource.cpp @@ -296,6 +296,149 @@ public: } protected: + + struct SourceInfo + { + ConstString function; + LineEntry line_entry; + + SourceInfo (const ConstString &name, const LineEntry &line_entry) : + function(name), + line_entry(line_entry) + { + } + + SourceInfo () : + function(), + line_entry() + { + } + + bool + IsValid () const + { + return (bool)function && line_entry.IsValid(); + } + + bool + operator == (const SourceInfo &rhs) const + { + return function == rhs.function && + line_entry.file == rhs.line_entry.file && + line_entry.line == rhs.line_entry.line; + } + + bool + operator != (const SourceInfo &rhs) const + { + return function != rhs.function || + line_entry.file != rhs.line_entry.file || + line_entry.line != rhs.line_entry.line; + } + + bool + operator < (const SourceInfo &rhs) const + { + if (function.GetCString() < rhs.function.GetCString()) + return true; + if (line_entry.file.GetDirectory().GetCString() < rhs.line_entry.file.GetDirectory().GetCString()) + return true; + if (line_entry.file.GetFilename().GetCString() < rhs.line_entry.file.GetFilename().GetCString()) + return true; + if (line_entry.line < rhs.line_entry.line) + return true; + return false; + } + }; + + size_t + DisplayFunctionSource (const SymbolContext &sc, + SourceInfo &source_info, + CommandReturnObject &result) + { + if (!source_info.IsValid()) + { + source_info.function = sc.GetFunctionName(); + source_info.line_entry = sc.GetFunctionStartLineEntry(); + } + + if (sc.function) + { + Target *target = m_exe_ctx.GetTargetPtr(); + + FileSpec start_file; + uint32_t start_line; + uint32_t end_line; + FileSpec end_file; + + if (sc.block == NULL) + { + // Not an inlined function + sc.function->GetStartLineSourceInfo (start_file, start_line); + if (start_line == 0) + { + result.AppendErrorWithFormat("Could not find line information for start of function: \"%s\".\n", source_info.function.GetCString()); + result.SetStatus (eReturnStatusFailed); + return 0; + } + sc.function->GetEndLineSourceInfo (end_file, end_line); + } + else + { + // We have an inlined function + start_file = source_info.line_entry.file; + start_line = source_info.line_entry.line; + end_line = start_line + m_options.num_lines; + } + + // This is a little hacky, but the first line table entry for a function points to the "{" that + // starts the function block. It would be nice to actually get the function + // declaration in there too. So back up a bit, but not further than what you're going to display. + uint32_t extra_lines; + if (m_options.num_lines >= 10) + extra_lines = 5; + else + extra_lines = m_options.num_lines/2; + uint32_t line_no; + if (start_line <= extra_lines) + line_no = 1; + else + line_no = start_line - extra_lines; + + // For fun, if the function is shorter than the number of lines we're supposed to display, + // only display the function... + if (end_line != 0) + { + if (m_options.num_lines > end_line - line_no) + m_options.num_lines = end_line - line_no + extra_lines; + } + + m_breakpoint_locations.Clear(); + + if (m_options.show_bp_locs) + { + const bool show_inlines = true; + m_breakpoint_locations.Reset (start_file, 0, show_inlines); + SearchFilter target_search_filter (m_exe_ctx.GetTargetSP()); + target_search_filter.Search (m_breakpoint_locations); + } + + result.AppendMessageWithFormat("File: %s\n", start_file.GetPath().c_str()); + return target->GetSourceManager().DisplaySourceLinesWithLineNumbers (start_file, + line_no, + 0, + m_options.num_lines, + "", + &result.GetOutputStream(), + GetBreakpointLocations ()); + } + else + { + result.AppendErrorWithFormat("Could not find function info for: \"%s\".\n", m_options.symbol_name.c_str()); + } + return 0; + } + bool DoExecute (Args& command, CommandReturnObject &result) { @@ -353,123 +496,48 @@ protected: return false; } - sc_list.GetContextAtIndex (0, sc); - FileSpec start_file; - uint32_t start_line; - uint32_t end_line; - FileSpec end_file; - if (sc.function != NULL) - { - sc.function->GetStartLineSourceInfo (start_file, start_line); - if (start_line == 0) - { - result.AppendErrorWithFormat("Could not find line information for start of function: \"%s\".\n", m_options.symbol_name.c_str()); - result.SetStatus (eReturnStatusFailed); - return false; - } - sc.function->GetEndLineSourceInfo (end_file, end_line); - } - else - { - result.AppendErrorWithFormat("Could not find function info for: \"%s\".\n", m_options.symbol_name.c_str()); - result.SetStatus (eReturnStatusFailed); - return false; - } - if (num_matches > 1) { - // This could either be because there are multiple functions of this name, in which case - // we'll have to specify this further... Or it could be because there are multiple inlined instances - // of one function. So run through the matches and if they all have the same file & line then we can just - // list one. - - bool found_multiple = false; + std::set<SourceInfo> source_match_set; - for (size_t i = 1; i < num_matches; i++) + bool displayed_something = false; + for (size_t i = 0; i < num_matches; i++) { - SymbolContext scratch_sc; - sc_list.GetContextAtIndex (i, scratch_sc); - if (scratch_sc.function != NULL) + sc_list.GetContextAtIndex (i, sc); + SourceInfo source_info (sc.GetFunctionName(), + sc.GetFunctionStartLineEntry()); + + if (source_info.IsValid()) { - FileSpec scratch_file; - uint32_t scratch_line; - scratch_sc.function->GetStartLineSourceInfo (scratch_file, scratch_line); - if (scratch_file != start_file - || scratch_line != start_line) + if (source_match_set.find(source_info) == source_match_set.end()) { - found_multiple = true; - break; + source_match_set.insert(source_info); + if (DisplayFunctionSource (sc, source_info, result)) + displayed_something = true; } } } - if (found_multiple) - { - StreamString s; - for (size_t i = 0; i < num_matches; i++) - { - SymbolContext scratch_sc; - sc_list.GetContextAtIndex (i, scratch_sc); - if (scratch_sc.function != NULL) - { - s.Printf("\n%lu: ", i); - scratch_sc.function->Dump (&s, true); - } - } - result.AppendErrorWithFormat("Multiple functions found matching: %s: \n%s\n", - m_options.symbol_name.c_str(), - s.GetData()); + + if (displayed_something) + result.SetStatus (eReturnStatusSuccessFinishResult); + else result.SetStatus (eReturnStatusFailed); - return false; - } } - - // This is a little hacky, but the first line table entry for a function points to the "{" that - // starts the function block. It would be nice to actually get the function - // declaration in there too. So back up a bit, but not further than what you're going to display. - uint32_t extra_lines; - if (m_options.num_lines >= 10) - extra_lines = 5; else - extra_lines = m_options.num_lines/2; - uint32_t line_no; - if (start_line <= extra_lines) - line_no = 1; - else - line_no = start_line - extra_lines; - - // For fun, if the function is shorter than the number of lines we're supposed to display, - // only display the function... - if (end_line != 0) { - if (m_options.num_lines > end_line - line_no) - m_options.num_lines = end_line - line_no + extra_lines; - } - - char path_buf[PATH_MAX]; - start_file.GetPath(path_buf, sizeof(path_buf)); - - if (m_options.show_bp_locs) - { - const bool show_inlines = true; - m_breakpoint_locations.Reset (start_file, 0, show_inlines); - SearchFilter target_search_filter (m_exe_ctx.GetTargetSP()); - target_search_filter.Search (m_breakpoint_locations); + sc_list.GetContextAtIndex (0, sc); + SourceInfo source_info; + + if (DisplayFunctionSource (sc, source_info, result)) + { + result.SetStatus (eReturnStatusSuccessFinishResult); + } + else + { + result.SetStatus (eReturnStatusFailed); + } } - else - m_breakpoint_locations.Clear(); - - result.AppendMessageWithFormat("File: %s\n", path_buf); - target->GetSourceManager().DisplaySourceLinesWithLineNumbers (start_file, - line_no, - 0, - m_options.num_lines, - "", - &result.GetOutputStream(), - GetBreakpointLocations ()); - - result.SetStatus (eReturnStatusSuccessFinishResult); - return true; - + return result.Succeeded(); } else if (m_options.address != LLDB_INVALID_ADDRESS) { diff --git a/lldb/source/Commands/CommandObjectTarget.cpp b/lldb/source/Commands/CommandObjectTarget.cpp index 7765352..cd2e7c3 100644 --- a/lldb/source/Commands/CommandObjectTarget.cpp +++ b/lldb/source/Commands/CommandObjectTarget.cpp @@ -1676,7 +1676,7 @@ LookupFunctionInModule (CommandInterpreter &interpreter, ConstString function_name (name); num_matches = module->FindFunctions (function_name, NULL, - eFunctionNameTypeBase | eFunctionNameTypeFull | eFunctionNameTypeMethod | eFunctionNameTypeSelector, + eFunctionNameTypeAuto, include_symbols, include_inlines, append, @@ -4376,11 +4376,14 @@ protected: // Make sure we load any scripting resources that may be embedded // in the debug info files in case the platform supports that. Error error; - module_sp->LoadScriptingResourceInTarget (target, error); - if (error.Fail()) + StreamString feedback_stream; + module_sp->LoadScriptingResourceInTarget (target, error,&feedback_stream); + if (error.Fail() && error.AsCString()) result.AppendWarningWithFormat("unable to load scripting data for module %s - error reported was %s", module_sp->GetFileSpec().GetFileNameStrippingExtension().GetCString(), error.AsCString()); + else if (feedback_stream.GetSize()) + result.AppendWarningWithFormat("%s",feedback_stream.GetData()); flush = true; result.SetStatus (eReturnStatusSuccessFinishResult); diff --git a/lldb/source/Commands/CommandObjectType.cpp b/lldb/source/Commands/CommandObjectType.cpp index 1fa364f..e69620e 100644 --- a/lldb/source/Commands/CommandObjectType.cpp +++ b/lldb/source/Commands/CommandObjectType.cpp @@ -179,7 +179,7 @@ public: } static bool - AddSummary(const ConstString& type_name, + AddSummary(ConstString type_name, lldb::TypeSummaryImplSP entry, SummaryFormatType type, std::string category, @@ -324,7 +324,7 @@ public: } static bool - AddSynth(const ConstString& type_name, + AddSynth(ConstString type_name, lldb::SyntheticChildrenSP entry, SynthFormatType type, std::string category_name, @@ -744,7 +744,9 @@ CommandObjectTypeFormatList_LoopCallback ( //------------------------------------------------------------------------- static const char *g_summary_addreader_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" - "def function (valobj,internal_dict):"; + "def function (valobj,internal_dict):\n" + " \"\"\"valobj: an SBValue which you want to provide a summary for\n" + " internal_dict: an LLDB support object not to be used\"\"\""; class TypeScriptAddInputReader : public InputReaderEZ { @@ -1338,7 +1340,7 @@ CommandObjectTypeSummaryAdd::DoExecute (Args& command, CommandReturnObject &resu } bool -CommandObjectTypeSummaryAdd::AddSummary(const ConstString& type_name, +CommandObjectTypeSummaryAdd::AddSummary(ConstString type_name, TypeSummaryImplSP entry, SummaryFormatType type, std::string category_name, @@ -1347,6 +1349,21 @@ CommandObjectTypeSummaryAdd::AddSummary(const ConstString& type_name, lldb::TypeCategoryImplSP category; DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()), category); + if (type == eRegularSummary) + { + std::string type_name_str(type_name.GetCString()); + if (type_name_str.compare(type_name_str.length() - 2, 2, "[]") == 0) + { + type_name_str.resize(type_name_str.length()-2); + if (type_name_str.back() != ' ') + type_name_str.append(" \\[[0-9]+\\]"); + else + type_name_str.append("\\[[0-9]+\\]"); + type_name.SetCString(type_name_str.c_str()); + type = eRegexSummary; + } + } + if (type == eRegexSummary) { RegularExpressionSP typeRX(new RegularExpression()); @@ -3573,15 +3590,30 @@ CommandObjectTypeSynthAdd::CommandObjectTypeSynthAdd (CommandInterpreter &interp } bool -CommandObjectTypeSynthAdd::AddSynth(const ConstString& type_name, - SyntheticChildrenSP entry, - SynthFormatType type, - std::string category_name, - Error* error) +CommandObjectTypeSynthAdd::AddSynth(ConstString type_name, + SyntheticChildrenSP entry, + SynthFormatType type, + std::string category_name, + Error* error) { lldb::TypeCategoryImplSP category; DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()), category); + if (type == eRegularSynth) + { + std::string type_name_str(type_name.GetCString()); + if (type_name_str.compare(type_name_str.length() - 2, 2, "[]") == 0) + { + type_name_str.resize(type_name_str.length()-2); + if (type_name_str.back() != ' ') + type_name_str.append(" \\[[0-9]+\\]"); + else + type_name_str.append("\\[[0-9]+\\]"); + type_name.SetCString(type_name_str.c_str()); + type = eRegularSynth; + } + } + if (category->AnyMatches(type_name, eFormatCategoryItemFilter | eFormatCategoryItemRegexFilter, false)) @@ -3752,7 +3784,7 @@ private: }; bool - AddFilter(const ConstString& type_name, + AddFilter(ConstString type_name, SyntheticChildrenSP entry, FilterFormatType type, std::string category_name, @@ -3761,6 +3793,21 @@ private: lldb::TypeCategoryImplSP category; DataVisualization::Categories::GetCategory(ConstString(category_name.c_str()), category); + if (type == eRegularFilter) + { + std::string type_name_str(type_name.GetCString()); + if (type_name_str.compare(type_name_str.length() - 2, 2, "[]") == 0) + { + type_name_str.resize(type_name_str.length()-2); + if (type_name_str.back() != ' ') + type_name_str.append(" \\[[0-9]+\\]"); + else + type_name_str.append("\\[[0-9]+\\]"); + type_name.SetCString(type_name_str.c_str()); + type = eRegexFilter; + } + } + if (category->AnyMatches(type_name, eFormatCategoryItemSynth | eFormatCategoryItemRegexSynth, false)) diff --git a/lldb/source/Core/AddressResolverName.cpp b/lldb/source/Core/AddressResolverName.cpp index c8b9db8..dd22e17 100644 --- a/lldb/source/Core/AddressResolverName.cpp +++ b/lldb/source/Core/AddressResolverName.cpp @@ -120,7 +120,7 @@ AddressResolverName::SearchCallback sym_list); context.module_sp->FindFunctions (m_func_name, NULL, - eFunctionNameTypeBase | eFunctionNameTypeFull | eFunctionNameTypeMethod | eFunctionNameTypeSelector, + eFunctionNameTypeAuto, include_symbols, include_inlines, append, diff --git a/lldb/source/Core/Debugger.cpp b/lldb/source/Core/Debugger.cpp index f6cffec..780c546 100644 --- a/lldb/source/Core/Debugger.cpp +++ b/lldb/source/Core/Debugger.cpp @@ -47,6 +47,7 @@ #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/StopInfo.h" +#include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" #include "lldb/Utility/AnsiTerminal.h" @@ -130,6 +131,7 @@ g_properties[] = { "term-width", OptionValue::eTypeSInt64 , true, 80 , NULL, NULL, "The maximum number of columns to use for displaying text." }, { "thread-format", OptionValue::eTypeString , true, 0 , DEFAULT_THREAD_FORMAT, NULL, "The default thread format string to use when displaying thread information." }, { "use-external-editor", OptionValue::eTypeBoolean, true, false, NULL, NULL, "Whether to use an external editor or not." }, +{ "use-color", OptionValue::eTypeBoolean, true, true , NULL, NULL, "Whether to use Ansi color codes or not." }, { NULL, OptionValue::eTypeInvalid, true, 0 , NULL, NULL, NULL } }; @@ -147,7 +149,8 @@ enum ePropertyStopLineCountBefore, ePropertyTerminalWidth, ePropertyThreadFormat, - ePropertyUseExternalEditor + ePropertyUseExternalEditor, + ePropertyUseColor, }; // @@ -170,15 +173,49 @@ Debugger::SetPropertyValue (const ExecutionContext *exe_ctx, const char *property_path, const char *value) { + bool is_load_script = strcmp(property_path,"target.load-script-from-symbol-file") == 0; + TargetSP target_sp; + LoadScriptFromSymFile load_script_old_value; + if (is_load_script && exe_ctx->GetTargetSP()) + { + target_sp = exe_ctx->GetTargetSP(); + load_script_old_value = target_sp->TargetProperties::GetLoadScriptFromSymbolFile(); + } Error error (Properties::SetPropertyValue (exe_ctx, op, property_path, value)); if (error.Success()) { + // FIXME it would be nice to have "on-change" callbacks for properties if (strcmp(property_path, g_properties[ePropertyPrompt].name) == 0) { const char *new_prompt = GetPrompt(); + std::string str = lldb_utility::ansi::FormatAnsiTerminalCodes (new_prompt, GetUseColor()); + if (str.length()) + new_prompt = str.c_str(); EventSP prompt_change_event_sp (new Event(CommandInterpreter::eBroadcastBitResetPrompt, new EventDataBytes (new_prompt))); GetCommandInterpreter().BroadcastEvent (prompt_change_event_sp); } + else if (strcmp(property_path, g_properties[ePropertyUseColor].name) == 0) + { + // use-color changed. Ping the prompt so it can reset the ansi terminal codes. + SetPrompt (GetPrompt()); + } + else if (is_load_script && target_sp && load_script_old_value == eLoadScriptFromSymFileWarn) + { + if (target_sp->TargetProperties::GetLoadScriptFromSymbolFile() == eLoadScriptFromSymFileTrue) + { + std::list<Error> errors; + StreamString feedback_stream; + if (!target_sp->LoadScriptingResources(errors,&feedback_stream)) + { + for (auto error : errors) + { + GetErrorStream().Printf("%s\n",error.AsCString()); + } + if (feedback_stream.GetSize()) + GetErrorStream().Printf("%s",feedback_stream.GetData()); + } + } + } } return error; } @@ -217,6 +254,9 @@ Debugger::SetPrompt(const char *p) const uint32_t idx = ePropertyPrompt; m_collection_sp->SetPropertyAtIndexAsString (NULL, idx, p); const char *new_prompt = GetPrompt(); + std::string str = lldb_utility::ansi::FormatAnsiTerminalCodes (new_prompt, GetUseColor()); + if (str.length()) + new_prompt = str.c_str(); EventSP prompt_change_event_sp (new Event(CommandInterpreter::eBroadcastBitResetPrompt, new EventDataBytes (new_prompt)));; GetCommandInterpreter().BroadcastEvent (prompt_change_event_sp); } @@ -270,6 +310,22 @@ Debugger::SetUseExternalEditor (bool b) return m_collection_sp->SetPropertyAtIndexAsBoolean (NULL, idx, b); } +bool +Debugger::GetUseColor () const +{ + const uint32_t idx = ePropertyUseColor; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); +} + +bool +Debugger::SetUseColor (bool b) +{ + const uint32_t idx = ePropertyUseColor; + bool ret = m_collection_sp->SetPropertyAtIndexAsBoolean (NULL, idx, b); + SetPrompt (GetPrompt()); + return ret; +} + uint32_t Debugger::GetStopSourceLineCount (bool before) const { @@ -602,6 +658,11 @@ Debugger::Debugger (lldb::LogOutputCallback log_callback, void *baton) : OptionValueSInt64 *term_width = m_collection_sp->GetPropertyAtIndexAsOptionValueSInt64 (NULL, ePropertyTerminalWidth); term_width->SetMinimumValue(10); term_width->SetMaximumValue(1024); + + // Turn off use-color if this is a dumb terminal. + const char *term = getenv ("TERM"); + if (term && !strcmp (term, "dumb")) + SetUseColor (false); } Debugger::~Debugger () @@ -1112,14 +1173,12 @@ TestPromptFormats (StackFrame *frame) SymbolContext sc (frame->GetSymbolContext(eSymbolContextEverything)); ExecutionContext exe_ctx; frame->CalculateExecutionContext(exe_ctx); - const char *end = NULL; - if (Debugger::FormatPrompt (prompt_format, &sc, &exe_ctx, &sc.line_entry.range.GetBaseAddress(), s, &end)) + if (Debugger::FormatPrompt (prompt_format, &sc, &exe_ctx, &sc.line_entry.range.GetBaseAddress(), s)) { printf("%s\n", s.GetData()); } else { - printf ("error: at '%s'\n", end); printf ("what we got: %s\n", s.GetData()); } } @@ -1296,8 +1355,8 @@ ExpandIndexedExpression (ValueObject* valobj, return item; } -bool -Debugger::FormatPrompt +static bool +FormatPromptRecurse ( const char *format, const SymbolContext *sc, @@ -1312,6 +1371,7 @@ Debugger::FormatPrompt bool success = true; const char *p; Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES)); + for (p = format; *p != '\0'; ++p) { if (realvalobj) @@ -1345,8 +1405,8 @@ Debugger::FormatPrompt StreamString sub_strm; ++p; // Skip the '{' - - if (FormatPrompt (p, sc, exe_ctx, addr, sub_strm, &p, valobj)) + + if (FormatPromptRecurse (p, sc, exe_ctx, addr, sub_strm, &p, valobj)) { // The stream had all it needed s.Write(sub_strm.GetData(), sub_strm.GetSize()); @@ -1684,7 +1744,7 @@ Debugger::FormatPrompt if (!special_directions) var_success &= item->DumpPrintableRepresentation(s,val_obj_display, custom_format); else - var_success &= FormatPrompt(special_directions, sc, exe_ctx, addr, s, NULL, item); + var_success &= FormatPromptRecurse(special_directions, sc, exe_ctx, addr, s, NULL, item); if (--max_num_children == 0) { @@ -1708,214 +1768,6 @@ Debugger::FormatPrompt format_addr = *addr; } } - else if (::strncmp (var_name_begin, "ansi.", strlen("ansi.")) == 0) - { - var_success = true; - var_name_begin += strlen("ansi."); // Skip the "ansi." - if (::strncmp (var_name_begin, "fg.", strlen("fg.")) == 0) - { - var_name_begin += strlen("fg."); // Skip the "fg." - if (::strncmp (var_name_begin, "black}", strlen("black}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_fg_black, - lldb_utility::ansi::k_escape_end); - } - else if (::strncmp (var_name_begin, "red}", strlen("red}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_fg_red, - lldb_utility::ansi::k_escape_end); - } - else if (::strncmp (var_name_begin, "green}", strlen("green}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_fg_green, - lldb_utility::ansi::k_escape_end); - } - else if (::strncmp (var_name_begin, "yellow}", strlen("yellow}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_fg_yellow, - lldb_utility::ansi::k_escape_end); - } - else if (::strncmp (var_name_begin, "blue}", strlen("blue}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_fg_blue, - lldb_utility::ansi::k_escape_end); - } - else if (::strncmp (var_name_begin, "purple}", strlen("purple}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_fg_purple, - lldb_utility::ansi::k_escape_end); - } - else if (::strncmp (var_name_begin, "cyan}", strlen("cyan}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_fg_cyan, - lldb_utility::ansi::k_escape_end); - } - else if (::strncmp (var_name_begin, "white}", strlen("white}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_fg_white, - lldb_utility::ansi::k_escape_end); - } - else - { - var_success = false; - } - } - else if (::strncmp (var_name_begin, "bg.", strlen("bg.")) == 0) - { - var_name_begin += strlen("bg."); // Skip the "bg." - if (::strncmp (var_name_begin, "black}", strlen("black}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_bg_black, - lldb_utility::ansi::k_escape_end); - } - else if (::strncmp (var_name_begin, "red}", strlen("red}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_bg_red, - lldb_utility::ansi::k_escape_end); - } - else if (::strncmp (var_name_begin, "green}", strlen("green}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_bg_green, - lldb_utility::ansi::k_escape_end); - } - else if (::strncmp (var_name_begin, "yellow}", strlen("yellow}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_bg_yellow, - lldb_utility::ansi::k_escape_end); - } - else if (::strncmp (var_name_begin, "blue}", strlen("blue}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_bg_blue, - lldb_utility::ansi::k_escape_end); - } - else if (::strncmp (var_name_begin, "purple}", strlen("purple}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_bg_purple, - lldb_utility::ansi::k_escape_end); - } - else if (::strncmp (var_name_begin, "cyan}", strlen("cyan}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_bg_cyan, - lldb_utility::ansi::k_escape_end); - } - else if (::strncmp (var_name_begin, "white}", strlen("white}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_bg_white, - lldb_utility::ansi::k_escape_end); - } - else - { - var_success = false; - } - } - else if (::strncmp (var_name_begin, "normal}", strlen ("normal}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_ctrl_normal, - lldb_utility::ansi::k_escape_end); - } - else if (::strncmp (var_name_begin, "bold}", strlen("bold}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_ctrl_bold, - lldb_utility::ansi::k_escape_end); - } - else if (::strncmp (var_name_begin, "faint}", strlen("faint}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_ctrl_faint, - lldb_utility::ansi::k_escape_end); - } - else if (::strncmp (var_name_begin, "italic}", strlen("italic}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_ctrl_italic, - lldb_utility::ansi::k_escape_end); - } - else if (::strncmp (var_name_begin, "underline}", strlen("underline}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_ctrl_underline, - lldb_utility::ansi::k_escape_end); - } - else if (::strncmp (var_name_begin, "slow-blink}", strlen("slow-blink}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_ctrl_slow_blink, - lldb_utility::ansi::k_escape_end); - } - else if (::strncmp (var_name_begin, "fast-blink}", strlen("fast-blink}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_ctrl_fast_blink, - lldb_utility::ansi::k_escape_end); - } - else if (::strncmp (var_name_begin, "negative}", strlen("negative}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_ctrl_negative, - lldb_utility::ansi::k_escape_end); - } - else if (::strncmp (var_name_begin, "conceal}", strlen("conceal}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_ctrl_conceal, - lldb_utility::ansi::k_escape_end); - - } - else if (::strncmp (var_name_begin, "crossed-out}", strlen("crossed-out}")) == 0) - { - s.Printf ("%s%s%s", - lldb_utility::ansi::k_escape_start, - lldb_utility::ansi::k_ctrl_crossed_out, - lldb_utility::ansi::k_escape_end); - } - else - { - var_success = false; - } - } break; case 'p': @@ -2604,6 +2456,24 @@ Debugger::FormatPrompt return success; } +bool +Debugger::FormatPrompt +( + const char *format, + const SymbolContext *sc, + const ExecutionContext *exe_ctx, + const Address *addr, + Stream &s, + ValueObject* valobj +) +{ + bool use_color = exe_ctx ? exe_ctx->GetTargetRef().GetDebugger().GetUseColor() : true; + std::string format_str = lldb_utility::ansi::FormatAnsiTerminalCodes (format, use_color); + if (format_str.length()) + format = format_str.c_str(); + return FormatPromptRecurse (format, sc, exe_ctx, addr, s, NULL, valobj); +} + void Debugger::SetLoggingCallback (lldb::LogOutputCallback log_callback, void *baton) { diff --git a/lldb/source/Core/Disassembler.cpp b/lldb/source/Core/Disassembler.cpp index b5497b4..0f5ed87 100644 --- a/lldb/source/Core/Disassembler.cpp +++ b/lldb/source/Core/Disassembler.cpp @@ -194,10 +194,7 @@ Disassembler::Disassemble { module->FindFunctions (name, NULL, - eFunctionNameTypeBase | - eFunctionNameTypeFull | - eFunctionNameTypeMethod | - eFunctionNameTypeSelector, + eFunctionNameTypeAuto, include_symbols, include_inlines, true, @@ -206,10 +203,7 @@ Disassembler::Disassemble else if (exe_ctx.GetTargetPtr()) { exe_ctx.GetTargetPtr()->GetImages().FindFunctions (name, - eFunctionNameTypeBase | - eFunctionNameTypeFull | - eFunctionNameTypeMethod | - eFunctionNameTypeSelector, + eFunctionNameTypeAuto, include_symbols, include_inlines, false, diff --git a/lldb/source/Core/Makefile b/lldb/source/Core/Makefile index 78b7e3d..d52443b 100644 --- a/lldb/source/Core/Makefile +++ b/lldb/source/Core/Makefile @@ -17,7 +17,7 @@ include $(LLDB_LEVEL)/Makefile # (cxa_demangle.cpp) uses dynamic_cast<> and GCC (at least 4.6 and 4.7) # complain if we try to compile it with -fno-rtti. $(info shell basename CXX is $(shell basename $(CXX))) -ifeq (g++,$(shell basename $(CXX))) +ifeq (g++,$(shell basename $(CXX) | colrm 4)) $(ObjDir)/cxa_demangle.o: Compile.CXX := $(filter-out -fno-rtti,$(Compile.CXX)) -frtti endif diff --git a/lldb/source/Core/Mangled.cpp b/lldb/source/Core/Mangled.cpp index 4852436..e6ab0c78 100644 --- a/lldb/source/Core/Mangled.cpp +++ b/lldb/source/Core/Mangled.cpp @@ -30,6 +30,7 @@ #include "lldb/Core/Timer.h" #include <ctype.h> #include <string.h> +#include <stdlib.h> using namespace lldb_private; diff --git a/lldb/source/Core/Module.cpp b/lldb/source/Core/Module.cpp index de39a98..d8420ab 100644 --- a/lldb/source/Core/Module.cpp +++ b/lldb/source/Core/Module.cpp @@ -218,6 +218,9 @@ Module::Module(const FileSpec& file_spec, Module::~Module() { + // Lock our module down while we tear everything down to make sure + // we don't get any access to the module while it is being destroyed + Mutex::Locker locker (m_mutex); // Scope for locker below... { Mutex::Locker locker (GetAllocationModuleCollectionMutex()); @@ -1235,7 +1238,7 @@ Module::IsLoadedInTarget (Target *target) } bool -Module::LoadScriptingResourceInTarget (Target *target, Error& error) +Module::LoadScriptingResourceInTarget (Target *target, Error& error, Stream* feedback_stream) { if (!target) { @@ -1243,7 +1246,7 @@ Module::LoadScriptingResourceInTarget (Target *target, Error& error) return false; } - bool shoud_load = target->TargetProperties::GetLoadScriptFromSymbolFile(); + LoadScriptFromSymFile shoud_load = target->TargetProperties::GetLoadScriptFromSymbolFile(); Debugger &debugger = target->GetDebugger(); const ScriptLanguage script_language = debugger.GetScriptLanguage(); @@ -1273,14 +1276,18 @@ Module::LoadScriptingResourceInTarget (Target *target, Error& error) FileSpec scripting_fspec (file_specs.GetFileSpecAtIndex(i)); if (scripting_fspec && scripting_fspec.Exists()) { - if (!shoud_load) + if (shoud_load == eLoadScriptFromSymFileFalse) + return false; + if (shoud_load == eLoadScriptFromSymFileWarn) { - error.SetErrorString("Target doesn't allow loading scripting resource. Please set target.load-script-from-symbol-file and retry."); + if (feedback_stream) + feedback_stream->Printf("warning: '%s' contains a debug script. To run this script in this debug session:\n\n command script import \"%s\"\n\nTo run all discovered debug scripts in this session:\n\n settings set target.load-script-from-symbol-file true\n" + ,GetFileSpec().GetFileNameStrippingExtension().GetCString(),scripting_fspec.GetPath().c_str()); return false; } StreamString scripting_stream; scripting_fspec.Dump(&scripting_stream); - const bool can_reload = false; + const bool can_reload = true; const bool init_lldb_globals = false; bool did_load = script_interpreter->LoadScriptingModule(scripting_stream.GetData(), can_reload, init_lldb_globals, error); if (!did_load) @@ -1438,8 +1445,19 @@ Module::PrepareForFunctionNameLookup (const ConstString &name, if (ObjCLanguageRuntime::IsPossibleObjCSelector(name_cstr)) lookup_name_type_mask |= eFunctionNameTypeSelector; - if (CPPLanguageRuntime::IsPossibleCPPCall(name_cstr, base_name_start, base_name_end)) + CPPLanguageRuntime::MethodName cpp_method (name); + llvm::StringRef basename (cpp_method.GetBasename()); + if (basename.empty()) + { + if (CPPLanguageRuntime::StripNamespacesFromVariableName (name_cstr, base_name_start, base_name_end)) + lookup_name_type_mask |= (eFunctionNameTypeMethod | eFunctionNameTypeBase); + } + else + { + base_name_start = basename.data(); + base_name_end = base_name_start + basename.size(); lookup_name_type_mask |= (eFunctionNameTypeMethod | eFunctionNameTypeBase); + } } } else @@ -1449,11 +1467,30 @@ Module::PrepareForFunctionNameLookup (const ConstString &name, { // If they've asked for a CPP method or function name and it can't be that, we don't // even need to search for CPP methods or names. - if (!CPPLanguageRuntime::IsPossibleCPPCall(name_cstr, base_name_start, base_name_end)) + CPPLanguageRuntime::MethodName cpp_method (name); + if (cpp_method.IsValid()) { - lookup_name_type_mask &= ~(eFunctionNameTypeMethod | eFunctionNameTypeBase); - if (lookup_name_type_mask == eFunctionNameTypeNone) - return; + llvm::StringRef basename (cpp_method.GetBasename()); + base_name_start = basename.data(); + base_name_end = base_name_start + basename.size(); + + if (!cpp_method.GetQualifiers().empty()) + { + // There is a "const" or other qualifer following the end of the fucntion parens, + // this can't be a eFunctionNameTypeBase + lookup_name_type_mask &= ~(eFunctionNameTypeBase); + if (lookup_name_type_mask == eFunctionNameTypeNone) + return; + } + } + else + { + if (!CPPLanguageRuntime::StripNamespacesFromVariableName (name_cstr, base_name_start, base_name_end)) + { + lookup_name_type_mask &= ~(eFunctionNameTypeMethod | eFunctionNameTypeBase); + if (lookup_name_type_mask == eFunctionNameTypeNone) + return; + } } } diff --git a/lldb/source/Core/ModuleList.cpp b/lldb/source/Core/ModuleList.cpp index 376d046e..07b20b8 100644 --- a/lldb/source/Core/ModuleList.cpp +++ b/lldb/source/Core/ModuleList.cpp @@ -1008,4 +1008,33 @@ ModuleList::RemoveSharedModuleIfOrphaned (const Module *module_ptr) return GetSharedModuleList ().RemoveIfOrphaned (module_ptr); } - +bool +ModuleList::LoadScriptingResourcesInTarget (Target *target, + std::list<Error>& errors, + Stream *feedback_stream, + bool continue_on_error) +{ + if (!target) + return false; + Mutex::Locker locker(m_modules_mutex); + for (auto module : m_modules) + { + Error error; + if (module) + { + if (!module->LoadScriptingResourceInTarget(target, error, feedback_stream)) + { + if (error.Fail() && error.AsCString()) + { + error.SetErrorStringWithFormat("unable to load scripting data for module %s - error reported was %s", + module->GetFileSpec().GetFileNameStrippingExtension().GetCString(), + error.AsCString()); + errors.push_back(error); + } + if (!continue_on_error) + return false; + } + } + } + return errors.size() == 0; +} diff --git a/lldb/source/Core/RegularExpression.cpp b/lldb/source/Core/RegularExpression.cpp index 731974a..4ccd774 100644 --- a/lldb/source/Core/RegularExpression.cpp +++ b/lldb/source/Core/RegularExpression.cpp @@ -129,7 +129,7 @@ bool RegularExpression::Execute(const char* s, Match *match, int execute_flags) const { int err = 1; - if (m_comp_err == 0) + if (s != NULL && m_comp_err == 0) { if (match) { diff --git a/lldb/source/Core/Scalar.cpp b/lldb/source/Core/Scalar.cpp index 16d8230c..e0a96248 100644 --- a/lldb/source/Core/Scalar.cpp +++ b/lldb/source/Core/Scalar.cpp @@ -715,6 +715,28 @@ Scalar::Cast(Scalar::Type type) return success; } +bool +Scalar::MakeSigned () +{ + bool success = false; + + switch (m_type) + { + case e_void: break; + case e_sint: success = true; break; + case e_uint: m_type = e_sint; success = true; break; + case e_slong: success = true; break; + case e_ulong: m_type = e_slong; success = true; break; + case e_slonglong: success = true; break; + case e_ulonglong: m_type = e_slonglong; success = true; break; + case e_float: success = true; break; + case e_double: success = true; break; + case e_long_double: success = true; break; + } + + return success; +} + int Scalar::SInt(int fail_value) const { diff --git a/lldb/source/Core/UUID.cpp b/lldb/source/Core/UUID.cpp index e84c13e..a512431 100644 --- a/lldb/source/Core/UUID.cpp +++ b/lldb/source/Core/UUID.cpp @@ -22,29 +22,30 @@ namespace lldb_private { -UUID::UUID() +UUID::UUID() : m_num_uuid_bytes(16) { ::memset (m_uuid, 0, sizeof(m_uuid)); } UUID::UUID(const UUID& rhs) { + m_num_uuid_bytes = rhs.m_num_uuid_bytes; ::memcpy (m_uuid, rhs.m_uuid, sizeof (m_uuid)); } UUID::UUID (const void *uuid_bytes, uint32_t num_uuid_bytes) { - if (uuid_bytes && num_uuid_bytes >= 16) - ::memcpy (m_uuid, uuid_bytes, sizeof (m_uuid)); - else - ::memset (m_uuid, 0, sizeof(m_uuid)); + SetBytes (uuid_bytes, num_uuid_bytes); } const UUID& UUID::operator=(const UUID& rhs) { if (this != &rhs) + { + m_num_uuid_bytes = rhs.m_num_uuid_bytes; ::memcpy (m_uuid, rhs.m_uuid, sizeof (m_uuid)); + } return *this; } @@ -55,6 +56,7 @@ UUID::~UUID() void UUID::Clear() { + m_num_uuid_bytes = 16; ::memset (m_uuid, 0, sizeof(m_uuid)); } @@ -70,12 +72,17 @@ UUID::GetAsString () const std::string result; char buf[64]; const uint8_t *u = (const uint8_t *)GetBytes(); - if (sizeof (buf) > snprintf (buf, + if (sizeof (buf) > (size_t)snprintf (buf, sizeof (buf), "%2.2X%2.2X%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X", u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7],u[8],u[9],u[10],u[11],u[12],u[13],u[14],u[15])) { result.append (buf); + if (m_num_uuid_bytes == 20) + { + if (sizeof (buf) > (size_t)snprintf (buf, sizeof (buf), "-%2.2X%2.2X%2.2X%2.2X", u[16],u[17],u[18],u[19])) + result.append (buf); + } } return result; } @@ -86,21 +93,37 @@ UUID::Dump (Stream *s) const const uint8_t *u = (const uint8_t *)GetBytes(); s->Printf ("%2.2X%2.2X%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X-%2.2X%2.2X%2.2X%2.2X%2.2X%2.2X", u[0],u[1],u[2],u[3],u[4],u[5],u[6],u[7],u[8],u[9],u[10],u[11],u[12],u[13],u[14],u[15]); + if (m_num_uuid_bytes == 20) + { + s->Printf ("-%2.2X%2.2X%2.2X%2.2X", u[16],u[17],u[18],u[19]); + } } void -UUID::SetBytes (const void *uuid_bytes) +UUID::SetBytes (const void *uuid_bytes, uint32_t num_uuid_bytes) { - if (uuid_bytes) - ::memcpy (m_uuid, uuid_bytes, sizeof (m_uuid)); + if (uuid_bytes && num_uuid_bytes >= 20) + { + m_num_uuid_bytes = 20; + ::memcpy (m_uuid, uuid_bytes, m_num_uuid_bytes); + } + else if (uuid_bytes && num_uuid_bytes >= 16) + { + m_num_uuid_bytes = 16; + ::memcpy (m_uuid, uuid_bytes, m_num_uuid_bytes); + m_uuid[16] = m_uuid[17] = m_uuid[18] = m_uuid[19] = 0; + } else + { + m_num_uuid_bytes = 16; ::memset (m_uuid, 0, sizeof(m_uuid)); + } } size_t UUID::GetByteSize() { - return sizeof(UUID::ValueType); + return m_num_uuid_bytes; } bool @@ -121,7 +144,11 @@ UUID::IsValid () const m_uuid[12] || m_uuid[13] || m_uuid[14] || - m_uuid[15]; + m_uuid[15] || + m_uuid[16] || + m_uuid[17] || + m_uuid[18] || + m_uuid[19]; } static inline int @@ -134,7 +161,7 @@ xdigit_to_int (char ch) } size_t -UUID::DecodeUUIDBytesFromCString (const char *p, ValueType &uuid_bytes, const char **end) +UUID::DecodeUUIDBytesFromCString (const char *p, ValueType &uuid_bytes, const char **end, uint32_t num_uuid_bytes) { size_t uuid_byte_idx = 0; if (p) @@ -153,7 +180,7 @@ UUID::DecodeUUIDBytesFromCString (const char *p, ValueType &uuid_bytes, const ch // Increment the byte that we are decoding within the UUID value // and break out if we are done - if (++uuid_byte_idx == 16) + if (++uuid_byte_idx == num_uuid_bytes) break; } else if (*p == '-') @@ -170,10 +197,13 @@ UUID::DecodeUUIDBytesFromCString (const char *p, ValueType &uuid_bytes, const ch } if (end) *end = p; + // Clear trailing bytes to 0. + for (uint32_t i = uuid_byte_idx; i < sizeof(ValueType); i++) + uuid_bytes[i] = 0; return uuid_byte_idx; } size_t -UUID::SetFromCString (const char *cstr) +UUID::SetFromCString (const char *cstr, uint32_t num_uuid_bytes) { if (cstr == NULL) return 0; @@ -184,11 +214,11 @@ UUID::SetFromCString (const char *cstr) while (isspace(*p)) ++p; - const size_t uuid_byte_idx = UUID::DecodeUUIDBytesFromCString (p, m_uuid, &p); + const size_t uuid_byte_idx = UUID::DecodeUUIDBytesFromCString (p, m_uuid, &p, num_uuid_bytes); // If we successfully decoded a UUID, return the amount of characters that // were consumed - if (uuid_byte_idx == 16) + if (uuid_byte_idx == num_uuid_bytes) return p - cstr; // Else return zero to indicate we were not able to parse a UUID value @@ -200,35 +230,35 @@ UUID::SetFromCString (const char *cstr) bool lldb_private::operator == (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) { - return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), lldb_private::UUID::GetByteSize()) == 0; + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) == 0; } bool lldb_private::operator != (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) { - return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), lldb_private::UUID::GetByteSize()) != 0; + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) != 0; } bool lldb_private::operator < (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) { - return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), lldb_private::UUID::GetByteSize()) < 0; + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) < 0; } bool lldb_private::operator <= (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) { - return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), lldb_private::UUID::GetByteSize()) <= 0; + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) <= 0; } bool lldb_private::operator > (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) { - return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), lldb_private::UUID::GetByteSize()) > 0; + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) > 0; } bool lldb_private::operator >= (const lldb_private::UUID &lhs, const lldb_private::UUID &rhs) { - return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), lldb_private::UUID::GetByteSize()) >= 0; + return ::memcmp (lhs.GetBytes(), rhs.GetBytes(), sizeof (lldb_private::UUID::ValueType)) >= 0; } diff --git a/lldb/source/Core/Value.cpp b/lldb/source/Core/Value.cpp index 7cae530..9c69608 100644 --- a/lldb/source/Core/Value.cpp +++ b/lldb/source/Core/Value.cpp @@ -339,8 +339,16 @@ Value::GetValueAsData (ExecutionContext *exe_ctx, Address file_so_addr; switch (m_value_type) { - default: - error.SetErrorStringWithFormat("invalid value type %i", m_value_type); + case eValueTypeVector: + if (m_context_type == eContextTypeClangType && ast_context) + { + ClangASTType ptr_type (ast_context, ClangASTContext::GetVoidPtrType(ast_context, false)); + uint64_t ptr_byte_size = ptr_type.GetClangTypeByteSize(); + data.SetAddressByteSize (ptr_byte_size); + } + else + data.SetAddressByteSize(sizeof(void *)); + data.SetData(m_vector.bytes, m_vector.length, m_vector.byte_order); break; case eValueTypeScalar: diff --git a/lldb/source/Core/ValueObject.cpp b/lldb/source/Core/ValueObject.cpp index 2692ecc..611a77c 100644 --- a/lldb/source/Core/ValueObject.cpp +++ b/lldb/source/Core/ValueObject.cpp @@ -82,6 +82,7 @@ ValueObject::ValueObject (ValueObject &parent) : m_synthetic_value(NULL), m_deref_valobj(NULL), m_format (eFormatDefault), + m_last_format (eFormatDefault), m_last_format_mgr_revision(0), m_type_summary_sp(), m_type_format_sp(), @@ -127,6 +128,7 @@ ValueObject::ValueObject (ExecutionContextScope *exe_scope, m_synthetic_value(NULL), m_deref_valobj(NULL), m_format (eFormatDefault), + m_last_format (eFormatDefault), m_last_format_mgr_revision(0), m_type_summary_sp(), m_type_format_sp(), @@ -1441,7 +1443,7 @@ ValueObject::GetValueAsCString (lldb::Format format, const char * ValueObject::GetValueAsCString () { - if (UpdateValueIfNeeded(true) && m_value_str.empty()) + if (UpdateValueIfNeeded(true)) { lldb::Format my_format = GetFormat(); if (my_format == lldb::eFormatDefault) @@ -1468,13 +1470,17 @@ ValueObject::GetValueAsCString () } } } - if (GetValueAsCString(my_format, m_value_str)) + if (my_format != m_last_format || m_value_str.empty()) { - if (!m_value_did_change && m_old_value_valid) + m_last_format = my_format; + if (GetValueAsCString(my_format, m_value_str)) { - // The value was gotten successfully, so we consider the - // value as changed if the value string differs - SetValueDidChange (m_old_value_str != m_value_str); + if (!m_value_did_change && m_old_value_valid) + { + // The value was gotten successfully, so we consider the + // value as changed if the value string differs + SetValueDidChange (m_old_value_str != m_value_str); + } } } } @@ -4315,6 +4321,7 @@ ValueObject::GetDynamicValueType () } return lldb::eNoDynamicValues; } + lldb::Format ValueObject::GetFormat () const { diff --git a/lldb/source/Core/ValueObjectVariable.cpp b/lldb/source/Core/ValueObjectVariable.cpp index daff740..09e2f1f 100644 --- a/lldb/source/Core/ValueObjectVariable.cpp +++ b/lldb/source/Core/ValueObjectVariable.cpp @@ -186,10 +186,8 @@ ValueObjectVariable::UpdateValue () switch (value_type) { - default: - assert(!"Unhandled expression result value kind..."); - break; - + case Value::eValueTypeVector: + // fall through case Value::eValueTypeScalar: // The variable value is in the Scalar value inside the m_value. // We can point our m_data right to it. diff --git a/lldb/source/DataFormatters/CF.cpp b/lldb/source/DataFormatters/CF.cpp index 8debc0e..bb1cbaf 100644 --- a/lldb/source/DataFormatters/CF.cpp +++ b/lldb/source/DataFormatters/CF.cpp @@ -7,6 +7,8 @@ // //===----------------------------------------------------------------------===// +#include "lldb/lldb-python.h" + #include "lldb/DataFormatters/CXXFormatterFunctions.h" #include "lldb/Core/DataBufferHeap.h" diff --git a/lldb/source/DataFormatters/CXXFormatterFunctions.cpp b/lldb/source/DataFormatters/CXXFormatterFunctions.cpp index b1d0f7d..348186f 100644 --- a/lldb/source/DataFormatters/CXXFormatterFunctions.cpp +++ b/lldb/source/DataFormatters/CXXFormatterFunctions.cpp @@ -7,6 +7,8 @@ // //===----------------------------------------------------------------------===// +#include "lldb/lldb-python.h" + #include "lldb/DataFormatters/CXXFormatterFunctions.h" #include "llvm/Support/ConvertUTF.h" @@ -902,7 +904,7 @@ lldb_private::formatters::NSDataSummaryProvider (ValueObject& valobj, Stream& st stream.Printf("%s%" PRIu64 " byte%s%s", (needs_at ? "@\"" : ""), value, - (value > 1 ? "s" : ""), + (value != 1 ? "s" : ""), (needs_at ? "\"" : "")); return true; @@ -939,8 +941,13 @@ ReadAsciiBufferAndDumpToStream (lldb::addr_t location, if (my_error.Fail()) return false; + + dest.Printf("%c%c",prefix_token,quote); + if (my_data_read) - dest.Printf("%c%c%s%c",prefix_token,quote,(char*)buffer_sp->GetBytes(),quote); + dest.Printf("%s",(char*)buffer_sp->GetBytes()); + + dest.Printf("%c",quote); return true; } diff --git a/lldb/source/DataFormatters/Cocoa.cpp b/lldb/source/DataFormatters/Cocoa.cpp index 0d7bcc9..2a1e909 100644 --- a/lldb/source/DataFormatters/Cocoa.cpp +++ b/lldb/source/DataFormatters/Cocoa.cpp @@ -7,6 +7,8 @@ // //===----------------------------------------------------------------------===// +#include "lldb/lldb-python.h" + #include "lldb/DataFormatters/CXXFormatterFunctions.h" #include "lldb/Core/DataBufferHeap.h" @@ -284,7 +286,7 @@ lldb_private::formatters::NSIndexSetSummaryProvider (ValueObject& valobj, Stream return false; } } while (false); - stream.Printf("%llu index%s", + stream.Printf("%" PRIu64 " index%s", count, (count == 1 ? "" : "es")); return true; diff --git a/lldb/source/DataFormatters/FormatManager.cpp b/lldb/source/DataFormatters/FormatManager.cpp index a6a5416..28c3f82 100644 --- a/lldb/source/DataFormatters/FormatManager.cpp +++ b/lldb/source/DataFormatters/FormatManager.cpp @@ -627,6 +627,9 @@ FormatManager::LoadLibcxxFormatters() AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxStdListSyntheticFrontEndCreator, "libc++ std::list synthetic children", ConstString("^std::__1::list<.+>(( )?&)?$"), stl_synth_flags, true); AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, "libc++ std::map synthetic children", ConstString("^std::__1::map<.+> >(( )?&)?$"), stl_synth_flags, true); AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxVectorBoolSyntheticFrontEndCreator, "libc++ std::vector<bool> synthetic children", ConstString("std::__1::vector<std::__1::allocator<bool> >"), stl_synth_flags); + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, "libc++ std::set synthetic children", ConstString("^std::__1::set<.+> >(( )?&)?$"), stl_synth_flags, true); + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, "libc++ std::multiset synthetic children", ConstString("^std::__1::multiset<.+> >(( )?&)?$"), stl_synth_flags, true); + AddCXXSynthetic(libcxx_category_sp, lldb_private::formatters::LibcxxStdMapSyntheticFrontEndCreator, "libc++ std::multimap synthetic children", ConstString("^std::__1::multimap<.+> >(( )?&)?$"), stl_synth_flags, true); libcxx_category_sp->GetRegexSyntheticNavigator()->Add(RegularExpressionSP(new RegularExpression("^(std::__1::)deque<.+>(( )?&)?$")), SyntheticChildrenSP(new ScriptedSyntheticChildren(stl_synth_flags, @@ -642,6 +645,9 @@ FormatManager::LoadLibcxxFormatters() AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::map summary provider", ConstString("^std::__1::map<.+>(( )?&)?$"), stl_summary_flags, true); AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::deque summary provider", ConstString("^std::__1::deque<.+>(( )?&)?$"), stl_summary_flags, true); AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::vector<bool> summary provider", ConstString("std::__1::vector<std::__1::allocator<bool> >"), stl_summary_flags); + AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::set summary provider", ConstString("^std::__1::set<.+>(( )?&)?$"), stl_summary_flags, true); + AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::multiset summary provider", ConstString("^std::__1::multiset<.+>(( )?&)?$"), stl_summary_flags, true); + AddCXXSummary(libcxx_category_sp, lldb_private::formatters::LibcxxContainerSummaryProvider, "libc++ std::multimap summary provider", ConstString("^std::__1::multimap<.+>(( )?&)?$"), stl_summary_flags, true); stl_summary_flags.SetSkipPointers(true); AddStringSummary(libcxx_category_sp, "{${var.__ptr_%S}} (strong=${var.count} weak=${var.weak_count})}", ConstString("^std::__1::shared_ptr<.+>(( )?&)?$"), stl_summary_flags, true); @@ -685,6 +691,8 @@ FormatManager::LoadSystemFormatters() sys_category_sp->GetSummaryNavigator()->Add(ConstString("char *"), string_format); sys_category_sp->GetSummaryNavigator()->Add(ConstString("const char *"), string_format); + sys_category_sp->GetSummaryNavigator()->Add(ConstString("unsigned char *"), string_format); + sys_category_sp->GetSummaryNavigator()->Add(ConstString("const unsigned char *"), string_format); sys_category_sp->GetRegexSummaryNavigator()->Add(any_size_char_arr, string_array_format); lldb::TypeSummaryImplSP ostype_summary(new StringSummaryFormat(TypeSummaryImpl::Flags().SetCascades(false) diff --git a/lldb/source/DataFormatters/LibCxx.cpp b/lldb/source/DataFormatters/LibCxx.cpp index 237fc7c..f7c2589 100644 --- a/lldb/source/DataFormatters/LibCxx.cpp +++ b/lldb/source/DataFormatters/LibCxx.cpp @@ -514,7 +514,7 @@ lldb_private::formatters::LibcxxContainerSummaryProvider (ValueObject& valobj, S uint64_t value = valobj.GetValueAsUnsigned(0); if (!value) return false; - stream.Printf("0x%016llx ", value); + stream.Printf("0x%016" PRIx64 " ", value); } - return Debugger::FormatPrompt("size=${svar%#}", NULL, NULL, NULL, stream, NULL, &valobj); + return Debugger::FormatPrompt("size=${svar%#}", NULL, NULL, NULL, stream, &valobj); } diff --git a/lldb/source/DataFormatters/LibCxxList.cpp b/lldb/source/DataFormatters/LibCxxList.cpp index 95cdf0f..d2669ea 100644 --- a/lldb/source/DataFormatters/LibCxxList.cpp +++ b/lldb/source/DataFormatters/LibCxxList.cpp @@ -7,6 +7,8 @@ // //===----------------------------------------------------------------------===// +#include "lldb/lldb-python.h" + #include "lldb/DataFormatters/CXXFormatterFunctions.h" #include "lldb/Core/DataBufferHeap.h" diff --git a/lldb/source/DataFormatters/LibCxxMap.cpp b/lldb/source/DataFormatters/LibCxxMap.cpp index 3f46c2a..0b0b8f7 100644 --- a/lldb/source/DataFormatters/LibCxxMap.cpp +++ b/lldb/source/DataFormatters/LibCxxMap.cpp @@ -7,6 +7,8 @@ // //===----------------------------------------------------------------------===// +#include "lldb/lldb-python.h" + #include "lldb/DataFormatters/CXXFormatterFunctions.h" #include "lldb/Core/DataBufferHeap.h" @@ -378,7 +380,7 @@ lldb_private::formatters::LibcxxStdMapSyntheticFrontEnd::Update() m_children.clear(); m_tree = m_backend.GetChildMemberWithName(ConstString("__tree_"), true).get(); if (!m_tree) - return NULL; + return false; m_root_node = m_tree->GetChildMemberWithName(ConstString("__begin_node_"), true).get(); return false; } diff --git a/lldb/source/DataFormatters/TypeSummary.cpp b/lldb/source/DataFormatters/TypeSummary.cpp index ae6fe7d..8c4d3f7 100644 --- a/lldb/source/DataFormatters/TypeSummary.cpp +++ b/lldb/source/DataFormatters/TypeSummary.cpp @@ -113,7 +113,7 @@ StringSummaryFormat::FormatObject (ValueObject *valobj, } else { - if (Debugger::FormatPrompt(m_format.c_str(), &sc, &exe_ctx, &sc.line_entry.range.GetBaseAddress(), s, NULL, valobj)) + if (Debugger::FormatPrompt(m_format.c_str(), &sc, &exe_ctx, &sc.line_entry.range.GetBaseAddress(), s, valobj)) { retval.assign(s.GetString()); return true; diff --git a/lldb/source/Expression/ClangExpressionDeclMap.cpp b/lldb/source/Expression/ClangExpressionDeclMap.cpp index dc1b89e..a1ccb00 100644 --- a/lldb/source/Expression/ClangExpressionDeclMap.cpp +++ b/lldb/source/Expression/ClangExpressionDeclMap.cpp @@ -1493,13 +1493,7 @@ ClangExpressionDeclMap::GetVariableValue return NULL; } } - else if (!var_location_expr.Evaluate(&m_parser_vars->m_exe_ctx, ast, NULL, NULL, NULL, loclist_base_load_addr, NULL, *var_location.get(), &err)) - { - if (log) - log->Printf("Error evaluating location: %s", err.AsCString()); - return NULL; - } - + void *type_to_use = NULL; if (parser_ast_context) diff --git a/lldb/source/Expression/ClangExpressionParser.cpp b/lldb/source/Expression/ClangExpressionParser.cpp index 765350c..6b3df9a 100644 --- a/lldb/source/Expression/ClangExpressionParser.cpp +++ b/lldb/source/Expression/ClangExpressionParser.cpp @@ -550,27 +550,30 @@ ClangExpressionParser::PrepareForExecution (lldb::addr_t &func_addr, if (execution_policy == eExecutionPolicyAlways || !can_interpret) { - if (m_expr.NeedsValidation() && (process && (!process->GetDynamicCheckers()))) - { - DynamicCheckerFunctions *dynamic_checkers = new DynamicCheckerFunctions(); - - StreamString install_errors; - - if (!dynamic_checkers->Install(install_errors, exe_ctx)) + if (m_expr.NeedsValidation() && process) + { + if (!process->GetDynamicCheckers()) { - if (install_errors.GetString().empty()) - err.SetErrorString ("couldn't install checkers, unknown error"); - else - err.SetErrorString (install_errors.GetString().c_str()); + DynamicCheckerFunctions *dynamic_checkers = new DynamicCheckerFunctions(); - return err; + StreamString install_errors; + + if (!dynamic_checkers->Install(install_errors, exe_ctx)) + { + if (install_errors.GetString().empty()) + err.SetErrorString ("couldn't install checkers, unknown error"); + else + err.SetErrorString (install_errors.GetString().c_str()); + + return err; + } + + process->SetDynamicCheckers(dynamic_checkers); + + if (log) + log->Printf("== [ClangUserExpression::Evaluate] Finished installing dynamic checkers =="); } - process->SetDynamicCheckers(dynamic_checkers); - - if (log) - log->Printf("== [ClangUserExpression::Evaluate] Finished installing dynamic checkers =="); - IRDynamicChecks ir_dynamic_checks(*process->GetDynamicCheckers(), function_name.AsCString()); if (!ir_dynamic_checks.runOnModule(*m_execution_unit->GetModule())) diff --git a/lldb/source/Expression/DWARFExpression.cpp b/lldb/source/Expression/DWARFExpression.cpp index 7951965..07cb688 100644 --- a/lldb/source/Expression/DWARFExpression.cpp +++ b/lldb/source/Expression/DWARFExpression.cpp @@ -1303,6 +1303,13 @@ DWARFExpression::Evaluate Error *error_ptr ) { + + if (opcodes_length == 0) + { + if (error_ptr) + error_ptr->SetErrorString ("no location, value may have been optimized out"); + return false; + } std::vector<Value> stack; Process *process = NULL; @@ -1328,7 +1335,7 @@ DWARFExpression::Evaluate if (!opcodes.ValidOffsetForDataOfSize(opcodes_offset, opcodes_length)) { if (error_ptr) - error_ptr->SetErrorString ("Invalid offset and/or length for opcodes buffer."); + error_ptr->SetErrorString ("invalid offset and/or length for opcodes buffer."); return false; } Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); diff --git a/lldb/source/Expression/IRInterpreter.cpp b/lldb/source/Expression/IRInterpreter.cpp index b3c35bf..9cb7bc0 100644 --- a/lldb/source/Expression/IRInterpreter.cpp +++ b/lldb/source/Expression/IRInterpreter.cpp @@ -151,10 +151,12 @@ public: if (constant) { - if (const ConstantInt *constant_int = dyn_cast<ConstantInt>(constant)) - { - return AssignToMatchType(scalar, constant_int->getLimitedValue(), value->getType()); - } + APInt value_apint; + + if (!ResolveConstantValue(value_apint, constant)) + return false; + + return AssignToMatchType(scalar, value_apint.getLimitedValue(), value->getType()); } else { @@ -496,6 +498,7 @@ IRInterpreter::CanInterpret (llvm::Module &module, case Instruction::Or: case Instruction::Ret: case Instruction::SDiv: + case Instruction::SExt: case Instruction::Shl: case Instruction::SRem: case Instruction::Store: @@ -662,12 +665,16 @@ IRInterpreter::Interpret (llvm::Module &module, result = L - R; break; case Instruction::SDiv: + L.MakeSigned(); + R.MakeSigned(); result = L / R; break; case Instruction::UDiv: result = L.GetRawBits64(0) / R.GetRawBits64(1); break; case Instruction::SRem: + L.MakeSigned(); + R.MakeSigned(); result = L % R; break; case Instruction::URem: @@ -778,8 +785,8 @@ IRInterpreter::Interpret (llvm::Module &module, if (log) { log->Printf("Interpreted an AllocaInst"); - log->Printf(" R : 0x%llx", R); - log->Printf(" P : 0x%llx", P); + log->Printf(" R : 0x%" PRIx64, R); + log->Printf(" P : 0x%" PRIx64, P); } } break; @@ -813,6 +820,39 @@ IRInterpreter::Interpret (llvm::Module &module, frame.AssignValue(inst, S, module); } break; + case Instruction::SExt: + { + const CastInst *cast_inst = dyn_cast<CastInst>(inst); + + if (!cast_inst) + { + if (log) + log->Printf("getOpcode() returns %s, but instruction is not a BitCastInst", cast_inst->getOpcodeName()); + error.SetErrorToGenericError(); + error.SetErrorString(interpreter_internal_error); + return false; + } + + Value *source = cast_inst->getOperand(0); + + lldb_private::Scalar S; + + if (!frame.EvaluateValue(S, source, module)) + { + if (log) + log->Printf("Couldn't evaluate %s", PrintValue(source).c_str()); + error.SetErrorToGenericError(); + error.SetErrorString(bad_value_error); + return false; + } + + S.MakeSigned(); + + lldb_private::Scalar S_signextend(S.SLongLong()); + + frame.AssignValue(inst, S_signextend, module); + } + break; case Instruction::Br: { const BranchInst *br_inst = dyn_cast<BranchInst>(inst); @@ -1004,15 +1044,23 @@ IRInterpreter::Interpret (llvm::Module &module, result = (L.GetRawBits64(0) <= R.GetRawBits64(0)); break; case CmpInst::ICMP_SGT: + L.MakeSigned(); + R.MakeSigned(); result = (L > R); break; case CmpInst::ICMP_SGE: + L.MakeSigned(); + R.MakeSigned(); result = (L >= R); break; case CmpInst::ICMP_SLT: + L.MakeSigned(); + R.MakeSigned(); result = (L < R); break; case CmpInst::ICMP_SLE: + L.MakeSigned(); + R.MakeSigned(); result = (L <= R); break; } @@ -1195,9 +1243,9 @@ IRInterpreter::Interpret (llvm::Module &module, if (log) { log->Printf("Interpreted a LoadInst"); - log->Printf(" P : 0x%llx", P); - log->Printf(" R : 0x%llx", R); - log->Printf(" D : 0x%llx", D); + log->Printf(" P : 0x%" PRIx64, P); + log->Printf(" R : 0x%" PRIx64, R); + log->Printf(" D : 0x%" PRIx64, D); } } break; @@ -1295,9 +1343,9 @@ IRInterpreter::Interpret (llvm::Module &module, if (log) { log->Printf("Interpreted a StoreInst"); - log->Printf(" D : 0x%llx", D); - log->Printf(" P : 0x%llx", P); - log->Printf(" R : 0x%llx", R); + log->Printf(" D : 0x%" PRIx64, D); + log->Printf(" P : 0x%" PRIx64, P); + log->Printf(" R : 0x%" PRIx64, R); } } break; diff --git a/lldb/source/Expression/IRMemoryMap.cpp b/lldb/source/Expression/IRMemoryMap.cpp index 3b19e84..e3f6178 100644 --- a/lldb/source/Expression/IRMemoryMap.cpp +++ b/lldb/source/Expression/IRMemoryMap.cpp @@ -31,19 +31,17 @@ IRMemoryMap::~IRMemoryMap () if (process_sp) { - for (AllocationMap::value_type &allocation : m_allocations) + AllocationMap::iterator iter; + + Error err; + + while ((iter = m_allocations.begin()) != m_allocations.end()) { - if (allocation.second.m_policy == eAllocationPolicyMirror || - allocation.second.m_policy == eAllocationPolicyProcessOnly || - (allocation.second.m_policy == eAllocationPolicyHostOnly && process_sp->CanJIT())) - process_sp->DeallocateMemory(allocation.second.m_process_alloc); - - if (lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) - { - log->Printf("IRMemoryMap::~IRMemoryMap deallocated [0x%llx..0x%llx)", - (uint64_t)allocation.second.m_process_start, - (uint64_t)allocation.second.m_process_start + (uint64_t)allocation.second.m_size); - } + err.Clear(); + if (iter->second.m_leak) + m_allocations.erase(iter); + else + Free(iter->first, err); } } } @@ -68,6 +66,14 @@ IRMemoryMap::FindSpace (size_t size) return ret; } + if (process_sp) + { + ret = process_sp->GetReservationCache().Find(size); + + if (ret != LLDB_INVALID_ADDRESS) + return ret; + } + for (int iterations = 0; iterations < 16; ++iterations) { lldb::addr_t candidate = LLDB_INVALID_ADDRESS; @@ -106,6 +112,11 @@ IRMemoryMap::FindSpace (size_t size) continue; ret = candidate; + + if (process_sp) + process_sp->GetReservationCache().Reserve(candidate, size); + + return ret; } return ret; @@ -349,7 +360,7 @@ IRMemoryMap::Malloc (size_t size, uint8_t alignment, uint32_t permissions, Alloc break; } - log->Printf("IRMemoryMap::Malloc (%llu, 0x%llx, 0x%llx, %s) -> 0x%llx", + log->Printf("IRMemoryMap::Malloc (%" PRIu64 ", 0x%" PRIx64 ", 0x%" PRIx64 ", %s) -> 0x%" PRIx64, (uint64_t)allocation_size, (uint64_t)alignment, (uint64_t)permissions, @@ -361,6 +372,25 @@ IRMemoryMap::Malloc (size_t size, uint8_t alignment, uint32_t permissions, Alloc } void +IRMemoryMap::Leak (lldb::addr_t process_address, Error &error) +{ + error.Clear(); + + AllocationMap::iterator iter = m_allocations.find(process_address); + + if (iter == m_allocations.end()) + { + error.SetErrorToGenericError(); + error.SetErrorString("Couldn't leak: allocation doesn't exist"); + return; + } + + Allocation &allocation = iter->second; + + allocation.m_leak = true; +} + +void IRMemoryMap::Free (lldb::addr_t process_address, Error &error) { error.Clear(); @@ -382,9 +412,14 @@ IRMemoryMap::Free (lldb::addr_t process_address, Error &error) case eAllocationPolicyHostOnly: { lldb::ProcessSP process_sp = m_process_wp.lock(); - if (process_sp && process_sp->CanJIT()) - process_sp->DeallocateMemory(allocation.m_process_alloc); // FindSpace allocated this for real - + if (process_sp) + { + if (process_sp->CanJIT()) + process_sp->DeallocateMemory(allocation.m_process_alloc); // FindSpace allocated this for real + else + process_sp->GetReservationCache().Unreserve(allocation.m_process_alloc); // FindSpace registered this memory + } + break; } case eAllocationPolicyMirror: @@ -398,7 +433,7 @@ IRMemoryMap::Free (lldb::addr_t process_address, Error &error) if (lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) { - log->Printf("IRMemoryMap::Free (0x%llx) freed [0x%llx..0x%llx)", + log->Printf("IRMemoryMap::Free (0x%" PRIx64 ") freed [0x%" PRIx64 "..0x%" PRIx64 ")", (uint64_t)process_address, iter->second.m_process_start, iter->second.m_process_start + iter->second.m_size); @@ -479,7 +514,7 @@ IRMemoryMap::WriteMemory (lldb::addr_t process_address, const uint8_t *bytes, si if (lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) { - log->Printf("IRMemoryMap::WriteMemory (0x%llx, 0x%llx, 0x%lld) went to [0x%llx..0x%llx)", + log->Printf("IRMemoryMap::WriteMemory (0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRId64 ") went to [0x%" PRIx64 "..0x%" PRIx64 ")", (uint64_t)process_address, (uint64_t)bytes, (uint64_t)size, @@ -612,7 +647,7 @@ IRMemoryMap::ReadMemory (uint8_t *bytes, lldb::addr_t process_address, size_t si if (lldb_private::Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) { - log->Printf("IRMemoryMap::ReadMemory (0x%llx, 0x%llx, 0x%lld) came from [0x%llx..0x%llx)", + log->Printf("IRMemoryMap::ReadMemory (0x%" PRIx64 ", 0x%" PRIx64 ", 0x%" PRId64 ") came from [0x%" PRIx64 "..0x%" PRIx64 ")", (uint64_t)process_address, (uint64_t)bytes, (uint64_t)size, @@ -642,7 +677,7 @@ IRMemoryMap::ReadScalarFromMemory (Scalar &scalar, lldb::addr_t process_address, { default: error.SetErrorToGenericError(); - error.SetErrorStringWithFormat("Couldn't read scalar: unsupported size %lld", (unsigned long long)size); + error.SetErrorStringWithFormat("Couldn't read scalar: unsupported size %" PRIu64, (uint64_t)size); return; case 1: scalar = extractor.GetU8(&offset); break; case 2: scalar = extractor.GetU16(&offset); break; @@ -686,7 +721,7 @@ IRMemoryMap::GetMemoryData (DataExtractor &extractor, lldb::addr_t process_addre if (iter == m_allocations.end()) { error.SetErrorToGenericError(); - error.SetErrorStringWithFormat("Couldn't find an allocation containing [0x%llx..0x%llx)", (unsigned long long)process_address, (unsigned long long)(process_address + size)); + error.SetErrorStringWithFormat("Couldn't find an allocation containing [0x%" PRIx64 "..0x%" PRIx64 ")", process_address, process_address + size); return; } diff --git a/lldb/source/Expression/Materializer.cpp b/lldb/source/Expression/Materializer.cpp index 20ccc1f..4bc4f81 100644 --- a/lldb/source/Expression/Materializer.cpp +++ b/lldb/source/Expression/Materializer.cpp @@ -89,8 +89,7 @@ public: if (!allocate_error.Success()) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't allocate a memory area to store %s: %s", m_persistent_variable_sp->GetName().GetCString(), allocate_error.AsCString()); + err.SetErrorStringWithFormat("couldn't allocate a memory area to store %s: %s", m_persistent_variable_sp->GetName().GetCString(), allocate_error.AsCString()); return; } @@ -110,7 +109,11 @@ public: // Clear the flag if the variable will never be deallocated. if (m_persistent_variable_sp->m_flags & ClangExpressionVariable::EVKeepInTarget) + { + Error leak_error; + map.Leak(mem, leak_error); m_persistent_variable_sp->m_flags &= ~ClangExpressionVariable::EVNeedsAllocation; + } // Write the contents of the variable to the area. @@ -123,8 +126,7 @@ public: if (!write_error.Success()) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat ("Couldn't write %s to the target: %s", m_persistent_variable_sp->GetName().AsCString(), + err.SetErrorStringWithFormat ("couldn't write %s to the target: %s", m_persistent_variable_sp->GetName().AsCString(), write_error.AsCString()); return; } @@ -140,8 +142,7 @@ public: if (!deallocate_error.Success()) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat ("Couldn't deallocate memory for %s: %s", m_persistent_variable_sp->GetName().GetCString(), deallocate_error.AsCString()); + err.SetErrorStringWithFormat ("couldn't deallocate memory for %s: %s", m_persistent_variable_sp->GetName().GetCString(), deallocate_error.AsCString()); } } @@ -151,7 +152,7 @@ public: if (log) { - log->Printf("EntityPersistentVariable::Materialize [process_address = 0x%llx, m_name = %s, m_flags = 0x%hx]", + log->Printf("EntityPersistentVariable::Materialize [process_address = 0x%" PRIx64 ", m_name = %s, m_flags = 0x%hx]", (uint64_t)process_address, m_persistent_variable_sp->GetName().AsCString(), m_persistent_variable_sp->m_flags); @@ -178,14 +179,12 @@ public: if (!write_error.Success()) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't write the location of %s to memory: %s", m_persistent_variable_sp->GetName().AsCString(), write_error.AsCString()); + err.SetErrorStringWithFormat("couldn't write the location of %s to memory: %s", m_persistent_variable_sp->GetName().AsCString(), write_error.AsCString()); } } else { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("No materialization happened for persistent variable %s", m_persistent_variable_sp->GetName().AsCString()); + err.SetErrorStringWithFormat("no materialization happened for persistent variable %s", m_persistent_variable_sp->GetName().AsCString()); return; } } @@ -197,7 +196,7 @@ public: if (log) { - log->Printf("EntityPersistentVariable::Dematerialize [process_address = 0x%llx, m_name = %s, m_flags = 0x%hx]", + log->Printf("EntityPersistentVariable::Dematerialize [process_address = 0x%" PRIx64 ", m_name = %s, m_flags = 0x%hx]", (uint64_t)process_address, m_persistent_variable_sp->GetName().AsCString(), m_persistent_variable_sp->m_flags); @@ -219,8 +218,7 @@ public: if (!read_error.Success()) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't read the address of program-allocated variable %s: %s", m_persistent_variable_sp->GetName().GetCString(), read_error.AsCString()); + err.SetErrorStringWithFormat("couldn't read the address of program-allocated variable %s: %s", m_persistent_variable_sp->GetName().GetCString(), read_error.AsCString()); return; } @@ -251,15 +249,13 @@ public: if (!m_persistent_variable_sp->m_live_sp) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't find the memory area used to store %s", m_persistent_variable_sp->GetName().GetCString()); + err.SetErrorStringWithFormat("couldn't find the memory area used to store %s", m_persistent_variable_sp->GetName().GetCString()); return; } if (m_persistent_variable_sp->m_live_sp->GetValue().GetValueAddressType() != eAddressTypeLoad) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("The address of the memory area for %s is in an incorrect format", m_persistent_variable_sp->GetName().GetCString()); + err.SetErrorStringWithFormat("the address of the memory area for %s is in an incorrect format", m_persistent_variable_sp->GetName().GetCString()); return; } @@ -282,8 +278,7 @@ public: if (!read_error.Success()) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat ("Couldn't read the contents of %s from memory: %s", m_persistent_variable_sp->GetName().GetCString(), read_error.AsCString()); + err.SetErrorStringWithFormat ("couldn't read the contents of %s from memory: %s", m_persistent_variable_sp->GetName().GetCString(), read_error.AsCString()); return; } @@ -292,8 +287,7 @@ public: } else { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("No dematerialization happened for persistent variable %s", m_persistent_variable_sp->GetName().AsCString()); + err.SetErrorStringWithFormat("no dematerialization happened for persistent variable %s", m_persistent_variable_sp->GetName().AsCString()); return; } @@ -324,7 +318,7 @@ public: Error err; - dump_stream.Printf("0x%llx: EntityPersistentVariable (%s)\n", (unsigned long long)process_address + m_offset, m_persistent_variable_sp->GetName().AsCString()); + dump_stream.Printf("0x%" PRIx64 ": EntityPersistentVariable (%s)\n", process_address + m_offset, m_persistent_variable_sp->GetName().AsCString()); { dump_stream.Printf("Pointer:\n"); @@ -421,7 +415,7 @@ public: if (log) { - log->Printf("EntityVariable::Materialize [process_address = 0x%llx, m_variable_sp = %s]", + log->Printf("EntityVariable::Materialize [process_address = 0x%" PRIx64 ", m_variable_sp = %s]", (uint64_t)process_address, m_variable_sp->GetName().AsCString()); } @@ -435,8 +429,7 @@ public: if (!valobj_sp) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't get a value object for variable %s", m_variable_sp->GetName().AsCString()); + err.SetErrorStringWithFormat("couldn't get a value object for variable %s", m_variable_sp->GetName().AsCString()); return; } @@ -452,8 +445,7 @@ public: if (!write_error.Success()) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't write the contents of reference variable %s to memory: %s", m_variable_sp->GetName().AsCString(), write_error.AsCString()); + err.SetErrorStringWithFormat("couldn't write the contents of reference variable %s to memory: %s", m_variable_sp->GetName().AsCString(), write_error.AsCString()); return; } } @@ -473,8 +465,7 @@ public: if (!write_error.Success()) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't write the address of variable %s to memory: %s", m_variable_sp->GetName().AsCString(), write_error.AsCString()); + err.SetErrorStringWithFormat("couldn't write the address of variable %s to memory: %s", m_variable_sp->GetName().AsCString(), write_error.AsCString()); return; } } @@ -485,15 +476,20 @@ public: if (m_temporary_allocation != LLDB_INVALID_ADDRESS) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Trying to create a temporary region for %s but one exists", m_variable_sp->GetName().AsCString()); + err.SetErrorStringWithFormat("trying to create a temporary region for %s but one exists", m_variable_sp->GetName().AsCString()); return; } if (data.GetByteSize() != m_variable_sp->GetType()->GetByteSize()) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Size of variable %s disagrees with the ValueObject's size", m_variable_sp->GetName().AsCString()); + if (data.GetByteSize() == 0 && m_variable_sp->LocationExpression().IsValid() == false) + { + err.SetErrorStringWithFormat("the variable '%s' has no location, it may have been optimized out", m_variable_sp->GetName().AsCString()); + } + else + { + err.SetErrorStringWithFormat("size of variable %s disagrees with the ValueObject's size", m_variable_sp->GetName().AsCString()); + } return; } @@ -507,8 +503,7 @@ public: if (!alloc_error.Success()) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't allocate a temporary region for %s: %s", m_variable_sp->GetName().AsCString(), alloc_error.AsCString()); + err.SetErrorStringWithFormat("couldn't allocate a temporary region for %s: %s", m_variable_sp->GetName().AsCString(), alloc_error.AsCString()); return; } @@ -518,8 +513,7 @@ public: if (!write_error.Success()) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't write to the temporary region for %s: %s", m_variable_sp->GetName().AsCString(), write_error.AsCString()); + err.SetErrorStringWithFormat("couldn't write to the temporary region for %s: %s", m_variable_sp->GetName().AsCString(), write_error.AsCString()); return; } @@ -529,8 +523,7 @@ public: if (!pointer_write_error.Success()) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't write the address of the temporary region for %s: %s", m_variable_sp->GetName().AsCString(), pointer_write_error.AsCString()); + err.SetErrorStringWithFormat("couldn't write the address of the temporary region for %s: %s", m_variable_sp->GetName().AsCString(), pointer_write_error.AsCString()); } } } @@ -543,7 +536,7 @@ public: if (log) { - log->Printf("EntityVariable::Dematerialize [process_address = 0x%llx, m_variable_sp = %s]", + log->Printf("EntityVariable::Dematerialize [process_address = 0x%" PRIx64 ", m_variable_sp = %s]", (uint64_t)process_address, m_variable_sp->GetName().AsCString()); } @@ -559,8 +552,7 @@ public: if (!valobj_sp) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't get a value object for variable %s", m_variable_sp->GetName().AsCString()); + err.SetErrorStringWithFormat("couldn't get a value object for variable %s", m_variable_sp->GetName().AsCString()); return; } @@ -572,8 +564,7 @@ public: if (!extract_error.Success()) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't get the data for variable %s", m_variable_sp->GetName().AsCString()); + err.SetErrorStringWithFormat("couldn't get the data for variable %s", m_variable_sp->GetName().AsCString()); return; } @@ -583,8 +574,7 @@ public: if (!set_error.Success()) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't write the new contents of %s back into the variable", m_variable_sp->GetName().AsCString()); + err.SetErrorStringWithFormat("couldn't write the new contents of %s back into the variable", m_variable_sp->GetName().AsCString()); return; } @@ -594,8 +584,7 @@ public: if (!free_error.Success()) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't free the temporary region for %s: %s", m_variable_sp->GetName().AsCString(), free_error.AsCString()); + err.SetErrorStringWithFormat("couldn't free the temporary region for %s: %s", m_variable_sp->GetName().AsCString(), free_error.AsCString()); return; } @@ -608,7 +597,7 @@ public: { StreamString dump_stream; - dump_stream.Printf("0x%llx: EntityVariable\n", (unsigned long long)process_address + m_offset); + dump_stream.Printf("0x%" PRIx64 ": EntityVariable\n", process_address + m_offset); Error err; @@ -727,7 +716,6 @@ public: { if (m_temporary_allocation != LLDB_INVALID_ADDRESS) { - err.SetErrorToGenericError(); err.SetErrorString("Trying to create a temporary region for the result but one exists"); return; } @@ -743,8 +731,7 @@ public: if (!alloc_error.Success()) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't allocate a temporary region for the result: %s", alloc_error.AsCString()); + err.SetErrorStringWithFormat("couldn't allocate a temporary region for the result: %s", alloc_error.AsCString()); return; } @@ -754,8 +741,7 @@ public: if (!pointer_write_error.Success()) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't write the address of the temporary region for the result: %s", pointer_write_error.AsCString()); + err.SetErrorStringWithFormat("couldn't write the address of the temporary region for the result: %s", pointer_write_error.AsCString()); } } } @@ -763,7 +749,6 @@ public: void Dematerialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb::addr_t process_address, lldb::addr_t frame_top, lldb::addr_t frame_bottom, Error &err) { - err.SetErrorToGenericError(); err.SetErrorString("Tried to detmaterialize a result variable with the normal Dematerialize method"); } @@ -777,7 +762,6 @@ public: if (!exe_scope) { - err.SetErrorToGenericError(); err.SetErrorString("Couldn't dematerialize a result variable: invalid execution context scope"); return; } @@ -789,7 +773,6 @@ public: if (!read_error.Success()) { - err.SetErrorToGenericError(); err.SetErrorString("Couldn't dematerialize a result variable: couldn't read its address"); return; } @@ -798,7 +781,6 @@ public: if (!target_sp) { - err.SetErrorToGenericError(); err.SetErrorString("Couldn't dematerialize a result variable: no target"); return; } @@ -815,8 +797,7 @@ public: if (!ret) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't dematerialize a result variable: failed to make persistent variable %s", name.AsCString()); + err.SetErrorStringWithFormat("couldn't dematerialize a result variable: failed to make persistent variable %s", name.AsCString()); return; } @@ -844,7 +825,6 @@ public: if (!read_error.Success()) { - err.SetErrorToGenericError(); err.SetErrorString("Couldn't dematerialize a result variable: couldn't read its memory"); return; } @@ -874,7 +854,7 @@ public: { StreamString dump_stream; - dump_stream.Printf("0x%llx: EntityResultVariable\n", (unsigned long long)process_address + m_offset); + dump_stream.Printf("0x%" PRIx64 ": EntityResultVariable\n", process_address + m_offset); Error err; @@ -991,7 +971,7 @@ public: if (log) { - log->Printf("EntitySymbol::Materialize [process_address = 0x%llx, m_symbol = %s]", + log->Printf("EntitySymbol::Materialize [process_address = 0x%" PRIx64 ", m_symbol = %s]", (uint64_t)process_address, m_symbol.GetName().AsCString()); } @@ -1007,8 +987,7 @@ public: if (!target_sp) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't resolve symbol %s because there is no target", m_symbol.GetName().AsCString()); + err.SetErrorStringWithFormat("couldn't resolve symbol %s because there is no target", m_symbol.GetName().AsCString()); return; } @@ -1023,8 +1002,7 @@ public: if (!pointer_write_error.Success()) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't write the address of symbol %s: %s", m_symbol.GetName().AsCString(), pointer_write_error.AsCString()); + err.SetErrorStringWithFormat("couldn't write the address of symbol %s: %s", m_symbol.GetName().AsCString(), pointer_write_error.AsCString()); return; } } @@ -1036,7 +1014,7 @@ public: if (log) { - log->Printf("EntitySymbol::Dematerialize [process_address = 0x%llx, m_symbol = %s]", + log->Printf("EntitySymbol::Dematerialize [process_address = 0x%" PRIx64 ", m_symbol = %s]", (uint64_t)process_address, m_symbol.GetName().AsCString()); } @@ -1050,7 +1028,7 @@ public: Error err; - dump_stream.Printf("0x%llx: EntitySymbol (%s)\n", (unsigned long long)process_address + m_offset, m_symbol.GetName().AsCString()); + dump_stream.Printf("0x%" PRIx64 ": EntitySymbol (%s)\n", process_address + m_offset, m_symbol.GetName().AsCString()); { dump_stream.Printf("Pointer:\n"); @@ -1111,7 +1089,7 @@ public: if (log) { - log->Printf("EntityRegister::Materialize [process_address = 0x%llx, m_register_info = %s]", + log->Printf("EntityRegister::Materialize [process_address = 0x%" PRIx64 ", m_register_info = %s]", (uint64_t)process_address, m_register_info.name); } @@ -1120,8 +1098,7 @@ public: if (!frame_sp.get()) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't materialize register %s without a stack frame", m_register_info.name); + err.SetErrorStringWithFormat("couldn't materialize register %s without a stack frame", m_register_info.name); return; } @@ -1129,8 +1106,7 @@ public: if (!reg_context_sp->ReadRegister(&m_register_info, reg_value)) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't read the value of register %s", m_register_info.name); + err.SetErrorStringWithFormat("couldn't read the value of register %s", m_register_info.name); return; } @@ -1138,15 +1114,13 @@ public: if (!reg_value.GetData(register_data)) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't get the data for register %s", m_register_info.name); + err.SetErrorStringWithFormat("couldn't get the data for register %s", m_register_info.name); return; } if (register_data.GetByteSize() != m_register_info.byte_size) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Data for register %s had size %llu but we expected %llu", m_register_info.name, (unsigned long long)register_data.GetByteSize(), (unsigned long long)m_register_info.byte_size); + err.SetErrorStringWithFormat("data for register %s had size %llu but we expected %llu", m_register_info.name, (unsigned long long)register_data.GetByteSize(), (unsigned long long)m_register_info.byte_size); return; } @@ -1156,8 +1130,7 @@ public: if (!write_error.Success()) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't write the contents of register %s: %s", m_register_info.name, write_error.AsCString()); + err.SetErrorStringWithFormat("couldn't write the contents of register %s: %s", m_register_info.name, write_error.AsCString()); return; } } @@ -1169,7 +1142,7 @@ public: if (log) { - log->Printf("EntityRegister::Dematerialize [process_address = 0x%llx, m_register_info = %s]", + log->Printf("EntityRegister::Dematerialize [process_address = 0x%" PRIx64 ", m_register_info = %s]", (uint64_t)process_address, m_register_info.name); } @@ -1180,8 +1153,7 @@ public: if (!frame_sp.get()) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't dematerialize register %s without a stack frame", m_register_info.name); + err.SetErrorStringWithFormat("couldn't dematerialize register %s without a stack frame", m_register_info.name); return; } @@ -1191,8 +1163,7 @@ public: if (!extract_error.Success()) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't get the data for register %s: %s", m_register_info.name, extract_error.AsCString()); + err.SetErrorStringWithFormat("couldn't get the data for register %s: %s", m_register_info.name, extract_error.AsCString()); return; } @@ -1200,8 +1171,7 @@ public: if (!reg_context_sp->WriteRegister(&m_register_info, register_value)) { - err.SetErrorToGenericError(); - err.SetErrorStringWithFormat("Couldn't write the value of register %s", m_register_info.name); + err.SetErrorStringWithFormat("couldn't write the value of register %s", m_register_info.name); return; } } @@ -1212,7 +1182,7 @@ public: Error err; - dump_stream.Printf("0x%llx: EntityRegister (%s)\n", (unsigned long long)process_address + m_offset, m_register_info.name); + dump_stream.Printf("0x%" PRIx64 ": EntityRegister (%s)\n", process_address + m_offset, m_register_info.name); { dump_stream.Printf("Value:\n"); @@ -1305,7 +1275,7 @@ Materializer::Materialize (lldb::StackFrameSP &frame_sp, IRMemoryMap &map, lldb: if (Log *log = lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) { - log->Printf("Materializer::Materialize (frame_sp = %p, process_address = 0x%llx) materialized:", frame_sp.get(), process_address); + log->Printf("Materializer::Materialize (frame_sp = %p, process_address = 0x%" PRIx64 ") materialized:", frame_sp.get(), process_address); for (EntityUP &entity_up : m_entities) entity_up->DumpToLog(map, process_address, log); } @@ -1337,7 +1307,7 @@ Materializer::Dematerializer::Dematerialize (Error &error, lldb::ClangExpression { if (Log *log =lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_EXPRESSIONS)) { - log->Printf("Materializer::Dematerialize (frame_sp = %p, process_address = 0x%llx) about to dematerialize:", frame_sp.get(), m_process_address); + log->Printf("Materializer::Dematerialize (frame_sp = %p, process_address = 0x%" PRIx64 ") about to dematerialize:", frame_sp.get(), m_process_address); for (EntityUP &entity_up : m_materializer->m_entities) entity_up->DumpToLog(*m_map, m_process_address, log); } diff --git a/lldb/source/Host/common/File.cpp b/lldb/source/Host/common/File.cpp index d051219..c0d3c29 100644 --- a/lldb/source/Host/common/File.cpp +++ b/lldb/source/Host/common/File.cpp @@ -318,58 +318,109 @@ File::GetFileSpec (FileSpec &file_spec) const return error; } -Error -File::SeekFromStart (off_t& offset) +off_t +File::SeekFromStart (off_t offset, Error *error_ptr) { - Error error; + off_t result = 0; if (DescriptorIsValid()) { - offset = ::lseek (m_descriptor, offset, SEEK_SET); + result = ::lseek (m_descriptor, offset, SEEK_SET); - if (offset == -1) - error.SetErrorToErrno(); + if (error_ptr) + { + if (result == -1) + error_ptr->SetErrorToErrno(); + else + error_ptr->Clear(); + } } - else + else if (StreamIsValid ()) { - error.SetErrorString("invalid file handle"); + result = ::fseek(m_stream, offset, SEEK_SET); + + if (error_ptr) + { + if (result == -1) + error_ptr->SetErrorToErrno(); + else + error_ptr->Clear(); + } } - return error; + else if (error_ptr) + { + error_ptr->SetErrorString("invalid file handle"); + } + return result; } -Error -File::SeekFromCurrent (off_t& offset) +off_t +File::SeekFromCurrent (off_t offset, Error *error_ptr) { - Error error; + off_t result = -1; if (DescriptorIsValid()) { - offset = ::lseek (m_descriptor, offset, SEEK_CUR); + result = ::lseek (m_descriptor, offset, SEEK_CUR); - if (offset == -1) - error.SetErrorToErrno(); + if (error_ptr) + { + if (result == -1) + error_ptr->SetErrorToErrno(); + else + error_ptr->Clear(); + } } - else + else if (StreamIsValid ()) { - error.SetErrorString("invalid file handle"); + result = ::fseek(m_stream, offset, SEEK_CUR); + + if (error_ptr) + { + if (result == -1) + error_ptr->SetErrorToErrno(); + else + error_ptr->Clear(); + } } - return error; + else if (error_ptr) + { + error_ptr->SetErrorString("invalid file handle"); + } + return result; } -Error -File::SeekFromEnd (off_t& offset) +off_t +File::SeekFromEnd (off_t offset, Error *error_ptr) { - Error error; + off_t result = -1; if (DescriptorIsValid()) { - offset = ::lseek (m_descriptor, offset, SEEK_END); + result = ::lseek (m_descriptor, offset, SEEK_END); - if (offset == -1) - error.SetErrorToErrno(); + if (error_ptr) + { + if (result == -1) + error_ptr->SetErrorToErrno(); + else + error_ptr->Clear(); + } } - else + else if (StreamIsValid ()) { - error.SetErrorString("invalid file handle"); + result = ::fseek(m_stream, offset, SEEK_END); + + if (error_ptr) + { + if (result == -1) + error_ptr->SetErrorToErrno(); + else + error_ptr->Clear(); + } } - return error; + else if (error_ptr) + { + error_ptr->SetErrorString("invalid file handle"); + } + return result; } Error diff --git a/lldb/source/Host/common/Host.cpp b/lldb/source/Host/common/Host.cpp index bc2b041..9d3614b 100644 --- a/lldb/source/Host/common/Host.cpp +++ b/lldb/source/Host/common/Host.cpp @@ -173,7 +173,11 @@ MonitorChildProcessThreadFunction (void *arg) if (errno == EINTR) continue; else + { + if (log) + log->Printf ("%s (arg = %p) thread exiting because waitpid failed (%s)...", __FUNCTION__, arg, strerror(errno)); break; + } } else if (wait_pid > 0) { @@ -190,8 +194,7 @@ MonitorChildProcessThreadFunction (void *arg) { exit_status = WEXITSTATUS(status); status_cstr = "EXITED"; - if (wait_pid == pid) - exited = true; + exited = true; } else if (WIFSIGNALED(status)) { @@ -230,12 +233,20 @@ MonitorChildProcessThreadFunction (void *arg) callback_return = callback (callback_baton, wait_pid, exited, signal, exit_status); // If our process exited, then this thread should exit - if (exited) + if (exited && wait_pid == pid) + { + if (log) + log->Printf ("%s (arg = %p) thread exiting because pid received exit signal...", __FUNCTION__, arg); break; + } // If the callback returns true, it means this process should // exit if (callback_return) + { + if (log) + log->Printf ("%s (arg = %p) thread exiting because callback returned true...", __FUNCTION__, arg); break; + } } } } @@ -1227,6 +1238,14 @@ Host::GetProcessInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info) } #endif +#if !defined(__linux__) +bool +Host::FindProcessThreads (const lldb::pid_t pid, TidMap &tids_to_attach) +{ + return false; +} +#endif + lldb::TargetSP Host::GetDummyTarget (lldb_private::Debugger &debugger) { diff --git a/lldb/source/Host/common/Mutex.cpp b/lldb/source/Host/common/Mutex.cpp index 069502b..39cd8c6 100644 --- a/lldb/source/Host/common/Mutex.cpp +++ b/lldb/source/Host/common/Mutex.cpp @@ -239,6 +239,7 @@ Mutex::Mutex (Mutex::Type type) : Mutex::~Mutex() { int err = ::pthread_mutex_destroy (&m_mutex); + assert(err == 0); #if ENABLE_MUTEX_ERROR_CHECKING if (err == 0) error_check_mutex (&m_mutex, eMutexActionDestroyed); diff --git a/lldb/source/Host/linux/Host.cpp b/lldb/source/Host/linux/Host.cpp index f1adf40..47b4550 100644 --- a/lldb/source/Host/linux/Host.cpp +++ b/lldb/source/Host/linux/Host.cpp @@ -25,6 +25,9 @@ #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/DataExtractor.h" +#include "lldb/Core/ModuleSpec.h" +#include "lldb/Symbol/ObjectFile.h" + using namespace lldb; using namespace lldb_private; @@ -47,6 +50,7 @@ typedef struct ProcessStatInfo // Get the process info with additional information from /proc/$PID/stat (like process state, and tracer pid). static bool GetProcessAndStatInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info, ProcessStatInfo &stat_info, lldb::pid_t &tracerpid); + namespace { @@ -291,6 +295,61 @@ Host::FindProcesses (const ProcessInstanceInfoMatch &match_info, ProcessInstance return process_infos.GetSize(); } +bool +Host::FindProcessThreads (const lldb::pid_t pid, TidMap &tids_to_attach) +{ + bool tids_changed = false; + static const char procdir[] = "/proc/"; + static const char taskdir[] = "/task/"; + std::string process_task_dir = procdir + std::to_string(pid) + taskdir; + DIR *dirproc = opendir (process_task_dir.c_str()); + + if (dirproc) + { + struct dirent *direntry = NULL; + while ((direntry = readdir (dirproc)) != NULL) + { + if (direntry->d_type != DT_DIR || !IsDirNumeric (direntry->d_name)) + continue; + + lldb::tid_t tid = atoi(direntry->d_name); + TidMap::iterator it = tids_to_attach.find(tid); + if (it == tids_to_attach.end()) + { + tids_to_attach.insert(TidPair(tid, false)); + tids_changed = true; + } + } + closedir (dirproc); + } + + return tids_changed; +} + +static bool +GetELFProcessCPUType (const char *exe_path, ProcessInstanceInfo &process_info) +{ + // Clear the architecture. + process_info.GetArchitecture().Clear(); + + ModuleSpecList specs; + FileSpec filespec (exe_path, false); + const size_t num_specs = ObjectFile::GetModuleSpecifications (filespec, 0, specs); + // GetModuleSpecifications() could fail if the executable has been deleted or is locked. + // But it shouldn't return more than 1 architecture. + assert(num_specs <= 1 && "Linux plugin supports only a single architecture"); + if (num_specs == 1) + { + ModuleSpec module_spec; + if (specs.GetModuleSpecAtIndex (0, module_spec) && module_spec.GetArchitecture().IsValid()) + { + process_info.GetArchitecture () = module_spec.GetArchitecture(); + return true; + } + } + return false; +} + static bool GetProcessAndStatInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info, ProcessStatInfo &stat_info, lldb::pid_t &tracerpid) { @@ -299,9 +358,6 @@ GetProcessAndStatInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info, Proce ::memset (&stat_info, 0, sizeof(stat_info)); stat_info.ppid = LLDB_INVALID_PROCESS_ID; - // Architecture is intentionally omitted because that's better resolved - // in other places (see ProcessPOSIX::DoAttachWithID(). - // Use special code here because proc/[pid]/exe is a symbolic link. char link_path[PATH_MAX]; char exe_path[PATH_MAX] = ""; @@ -323,6 +379,10 @@ GetProcessAndStatInfo (lldb::pid_t pid, ProcessInstanceInfo &process_info, Proce { exe_path[len - deleted_len] = 0; } + else + { + GetELFProcessCPUType (exe_path, process_info); + } process_info.SetProcessID(pid); process_info.GetExecutableFile().SetFile(exe_path, false); diff --git a/lldb/source/Host/macosx/launcherXPCService/LauncherRootXPCService-Info.plist b/lldb/source/Host/macosx/launcherXPCService/LauncherRootXPCService-Info.plist index db01bb8..b4f5000 100644 --- a/lldb/source/Host/macosx/launcherXPCService/LauncherRootXPCService-Info.plist +++ b/lldb/source/Host/macosx/launcherXPCService/LauncherRootXPCService-Info.plist @@ -39,6 +39,8 @@ <string>root</string> <key>ServiceType</key> <string>Application</string> + <key>JoinExistingSession</key> + <true/> </dict> </dict> </plist> diff --git a/lldb/source/Interpreter/CommandInterpreter.cpp b/lldb/source/Interpreter/CommandInterpreter.cpp index 7860eda..a57db48 100644 --- a/lldb/source/Interpreter/CommandInterpreter.cpp +++ b/lldb/source/Interpreter/CommandInterpreter.cpp @@ -2804,27 +2804,52 @@ CommandInterpreter::OutputHelpText (Stream &strm, void CommandInterpreter::FindCommandsForApropos (const char *search_word, StringList &commands_found, - StringList &commands_help) + StringList &commands_help, bool search_builtin_commands, bool search_user_commands) { CommandObject::CommandMap::const_iterator pos; - for (pos = m_command_dict.begin(); pos != m_command_dict.end(); ++pos) + if (search_builtin_commands) { - const char *command_name = pos->first.c_str(); - CommandObject *cmd_obj = pos->second.get(); - - if (cmd_obj->HelpTextContainsWord (search_word)) + for (pos = m_command_dict.begin(); pos != m_command_dict.end(); ++pos) { - commands_found.AppendString (command_name); - commands_help.AppendString (cmd_obj->GetHelp()); + const char *command_name = pos->first.c_str(); + CommandObject *cmd_obj = pos->second.get(); + + if (cmd_obj->HelpTextContainsWord (search_word)) + { + commands_found.AppendString (command_name); + commands_help.AppendString (cmd_obj->GetHelp()); + } + + if (cmd_obj->IsMultiwordObject()) + cmd_obj->AproposAllSubCommands (command_name, + search_word, + commands_found, + commands_help); + } + } + + if (search_user_commands) + { + for (pos = m_user_dict.begin(); pos != m_user_dict.end(); ++pos) + { + const char *command_name = pos->first.c_str(); + CommandObject *cmd_obj = pos->second.get(); - if (cmd_obj->IsMultiwordObject()) - cmd_obj->AproposAllSubCommands (command_name, - search_word, - commands_found, - commands_help); - + if (cmd_obj->HelpTextContainsWord (search_word)) + { + commands_found.AppendString (command_name); + commands_help.AppendString (cmd_obj->GetHelp()); + } + + if (cmd_obj->IsMultiwordObject()) + cmd_obj->AproposAllSubCommands (command_name, + search_word, + commands_found, + commands_help); + + } } } diff --git a/lldb/source/Interpreter/PythonDataObjects.cpp b/lldb/source/Interpreter/PythonDataObjects.cpp index 8cd4db9..e59e75a 100644 --- a/lldb/source/Interpreter/PythonDataObjects.cpp +++ b/lldb/source/Interpreter/PythonDataObjects.cpp @@ -21,6 +21,10 @@ #include <Python.h> #endif +#include <stdio.h> + +#include "lldb/Core/Stream.h" +#include "lldb/Host/File.h" #include "lldb/Interpreter/PythonDataObjects.h" #include "lldb/Interpreter/ScriptInterpreter.h" @@ -37,6 +41,31 @@ PythonObject::PythonObject (const lldb::ScriptInterpreterObjectSP &script_object Reset ((PyObject *)script_object_sp->GetObject()); } +void +PythonObject::Dump (Stream &strm) const +{ + if (m_py_obj) + { + FILE *file = ::tmpfile(); + if (file) + { + ::PyObject_Print (m_py_obj, file, 0); + const long length = ftell (file); + if (length) + { + ::rewind(file); + std::vector<char> file_contents (length,'\0'); + const size_t length_read = ::fread (file_contents.data(), 1, file_contents.size(), file); + if (length_read > 0) + strm.Write (file_contents.data(), length_read); + } + ::fclose (file); + } + } + else + strm.PutCString ("NULL"); +} + //---------------------------------------------------------------------- // PythonString //---------------------------------------------------------------------- diff --git a/lldb/source/Interpreter/ScriptInterpreterPython.cpp b/lldb/source/Interpreter/ScriptInterpreterPython.cpp index 5b6e0f5..da80e0c 100644 --- a/lldb/source/Interpreter/ScriptInterpreterPython.cpp +++ b/lldb/source/Interpreter/ScriptInterpreterPython.cpp @@ -1248,6 +1248,12 @@ ScriptInterpreterPython::ExecuteMultipleLines (const char *in_string, const Exec static const char *g_reader_instructions = "Enter your Python command(s). Type 'DONE' to end."; +static const char *g_bkpt_command_reader_instructions = "Enter your Python command(s). Type 'DONE' to end.\n" + "def function(frame,bp_loc,internal_dict):\n" + " \"\"\"frame: the SBFrame for the location at which you stopped\n" + " bp_loc: an SBBreakpointLocation for the breakpoint location information\n" + " internal_dict: an LLDB support object not to be used\"\"\""; + size_t ScriptInterpreterPython::GenerateBreakpointOptionsCommandCallback ( @@ -1270,7 +1276,7 @@ ScriptInterpreterPython::GenerateBreakpointOptionsCommandCallback commands_in_progress.Clear(); if (!batch_mode) { - out_stream->Printf ("%s\n", g_reader_instructions); + out_stream->Printf ("%s\n", g_bkpt_command_reader_instructions); if (reader.GetPrompt()) out_stream->Printf ("%s", reader.GetPrompt()); out_stream->Flush (); @@ -1497,7 +1503,7 @@ ScriptInterpreterPython::CollectDataForBreakpointCommandCallback (BreakpointOpti bp_options, // baton eInputReaderGranularityLine, // token size, for feeding data to callback function "DONE", // end token - "> ", // prompt + " ", // prompt true); // echo input if (err.Success()) @@ -2666,7 +2672,10 @@ ScriptInterpreterPython::LoadScriptingModule (const char* pathname, // now actually do the import command_stream.Clear(); - command_stream.Printf("import %s",basename.c_str()); + if (was_imported) + command_stream.Printf("reload(%s)",basename.c_str()); + else + command_stream.Printf("import %s",basename.c_str()); bool import_retval = ExecuteMultipleLines(command_stream.GetData(), ScriptInterpreter::ExecuteScriptOptions().SetEnableIO(false).SetSetLLDBGlobals(false).SetMaskoutErrors(false)); PyObject* py_error = PyErr_Occurred(); // per Python docs: "you do not need to Py_DECREF()" the return of this function diff --git a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h index c34ac65..8091710 100644 --- a/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h +++ b/lldb/source/Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h @@ -52,9 +52,6 @@ public: DynamicLoaderDarwinKernel (lldb_private::Process *process, lldb::addr_t kernel_addr); - static lldb::addr_t - SearchForDarwinKernel (lldb_private::Process *process); - virtual ~DynamicLoaderDarwinKernel (); @@ -344,6 +341,9 @@ protected: KextImageInfo::collection &image_infos); static lldb::addr_t + SearchForDarwinKernel (lldb_private::Process *process); + + static lldb::addr_t SearchForKernelAtSameLoadAddr (lldb_private::Process *process); static lldb::addr_t diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp index 951654b..17d2750 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntime.cpp @@ -38,6 +38,8 @@ using namespace lldb; using namespace lldb_private; +#define PO_FUNCTION_TIMEOUT_USEC 15*1000*1000 + bool AppleObjCRuntime::GetObjectDescription (Stream &str, ValueObject &valobj) { @@ -143,7 +145,7 @@ AppleObjCRuntime::GetObjectDescription (Stream &strm, Value &value, ExecutionCon &wrapper_struct_addr, error_stream, stop_others, - 0 /* no timeout */, + PO_FUNCTION_TIMEOUT_USEC /* 15 secs timeout */, try_all_threads, unwind_on_error, ignore_breakpoints, diff --git a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp index 2df1ac9..9da839e 100644 --- a/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp +++ b/lldb/source/Plugins/LanguageRuntime/ObjC/AppleObjCRuntime/AppleObjCRuntimeV2.cpp @@ -2599,7 +2599,7 @@ AppleObjCRuntimeV2::TaggedPointerVendorRuntimeAssisted::GetClassDescriptor (lldb Error error; uintptr_t slot_data = process->ReadPointerFromMemory(slot_ptr, error); if (error.Fail() || slot_data == 0 || slot_data == LLDB_INVALID_ADDRESS) - return false; + return nullptr; actual_class_descriptor_sp = m_runtime.GetClassDescriptor(slot_data); if (!actual_class_descriptor_sp) return ObjCLanguageRuntime::ClassDescriptorSP(); diff --git a/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp index 109e429..fdd6186 100644 --- a/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp +++ b/lldb/source/Plugins/ObjectContainer/BSD-Archive/ObjectContainerBSDArchive.cpp @@ -457,7 +457,7 @@ ObjectContainerBSDArchive::GetObjectFile (const FileSpec *file) module_sp->GetObjectModificationTime()); if (object) { - lldb::offset_t data_offset = m_offset + object->ar_file_offset; + lldb::offset_t data_offset = object->ar_file_offset; return ObjectFile::FindPlugin (module_sp, file, m_offset + object->ar_file_offset, diff --git a/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.cpp b/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.cpp index 9387e5d..a30a4c6 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.cpp +++ b/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.cpp @@ -305,7 +305,7 @@ ELFSymbol::Dump (lldb_private::Stream *s, const lldb_private::DataExtractor *strtab_data, const lldb_private::SectionList *section_list) { - s->Printf("[%3u] 0x%16.16llx 0x%16.16llx 0x%8.8x 0x%2.2x (%-10s %-13s) 0x%2.2x 0x%4.4x (%-10s) %s\n", + s->Printf("[%3u] 0x%16.16" PRIx64 " 0x%16.16" PRIx64 " 0x%8.8x 0x%2.2x (%-10s %-13s) 0x%2.2x 0x%4.4x (%-10s) %s\n", idx, st_value, st_size, diff --git a/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.h b/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.h index 11ab1dc..aa2c16b 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.h +++ b/lldb/source/Plugins/ObjectFile/ELF/ELFHeader.h @@ -107,7 +107,7 @@ struct ELFHeader GetRelocationJumpSlotType() const; //-------------------------------------------------------------------------- - /// Parse an ELFSectionHeader entry starting at position \p offset and + /// Parse an ELFHeader entry starting at position \p offset and /// update the data extractor with the address size and byte order /// attributes as defined by the header. /// @@ -120,7 +120,7 @@ struct ELFHeader /// advanced by the number of bytes read. /// /// @return - /// True if the ELFSectionHeader was successfully read and false + /// True if the ELFHeader was successfully read and false /// otherwise. bool Parse(lldb_private::DataExtractor &data, lldb::offset_t *offset); diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp index f4af192..9e1febb 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.cpp @@ -222,6 +222,18 @@ ObjectFileELF::CreateMemoryInstance (const lldb::ModuleSP &module_sp, return NULL; } +bool +ObjectFileELF::MagicBytesMatch (DataBufferSP& data_sp, + lldb::addr_t data_offset, + lldb::addr_t data_length) +{ + if (data_sp && data_sp->GetByteSize() > (llvm::ELF::EI_NIDENT + data_offset)) + { + const uint8_t *magic = data_sp->GetBytes() + data_offset; + return ELFHeader::MagicBytesMatch(magic); + } + return false; +} size_t ObjectFileELF::GetModuleSpecifications (const lldb_private::FileSpec& file, @@ -231,7 +243,39 @@ ObjectFileELF::GetModuleSpecifications (const lldb_private::FileSpec& file, lldb::offset_t length, lldb_private::ModuleSpecList &specs) { - return 0; + const size_t initial_count = specs.GetSize(); + + if (ObjectFileELF::MagicBytesMatch(data_sp, 0, data_sp->GetByteSize())) + { + DataExtractor data; + data.SetData(data_sp); + elf::ELFHeader header; + if (header.Parse(data, &data_offset)) + { + if (data_sp) + { + ModuleSpec spec; + spec.GetFileSpec() = file; + spec.GetArchitecture().SetArchitecture(eArchTypeELF, + header.e_machine, + LLDB_INVALID_CPUTYPE); + if (spec.GetArchitecture().IsValid()) + { + // We could parse the ABI tag information (in .note, .notes, or .note.ABI-tag) to get the + // machine information. However, we'd have to read a good bit of the rest of the file, + // and this info isn't guaranteed to exist or be correct. More details here: + // http://refspecs.linuxfoundation.org/LSB_1.2.0/gLSB/noteabitag.html + // Instead of passing potentially incorrect information down the pipeline, grab + // the host information and use it. + spec.GetArchitecture().GetTriple().setOSName (Host::GetOSString().GetCString()); + spec.GetArchitecture().GetTriple().setVendorName(Host::GetVendorString().GetCString()); + specs.Append(spec); + } + } + } + } + + return specs.GetSize() - initial_count; } //------------------------------------------------------------------ @@ -311,14 +355,19 @@ ObjectFileELF::SectionIndex(const SectionHeaderCollConstIter &I) const bool ObjectFileELF::ParseHeader() { - lldb::offset_t offset = GetFileOffset(); + lldb::offset_t offset = 0; return m_header.Parse(m_data, &offset); } bool ObjectFileELF::GetUUID(lldb_private::UUID* uuid) { - // FIXME: Return MD5 sum here. See comment in ObjectFile.h. + if (m_uuid.IsValid()) + { + *uuid = m_uuid; + return true; + } + // FIXME: Return MD5 sum here. See comment in ObjectFile.h. return false; } @@ -619,6 +668,51 @@ ObjectFileELF::GetSectionHeaderByIndex(lldb::user_id_t id) return NULL; } +static bool +ParseNoteGNUBuildID(DataExtractor& data, lldb_private::UUID& uuid) +{ + // Try to parse the note section (ie .note.gnu.build-id|.notes|.note|...) and get the build id. + // BuildID documentation: https://fedoraproject.org/wiki/Releases/FeatureBuildId + struct + { + uint32_t name_len; // Length of note name + uint32_t desc_len; // Length of note descriptor + uint32_t type; // Type of note (1 is ABI_TAG, 3 is BUILD_ID) + } notehdr; + lldb::offset_t offset = 0; + static const uint32_t g_gnu_build_id = 3; // NT_GNU_BUILD_ID from elf.h + + while (true) + { + if (data.GetU32 (&offset, ¬ehdr, 3) == NULL) + return false; + + notehdr.name_len = llvm::RoundUpToAlignment (notehdr.name_len, 4); + notehdr.desc_len = llvm::RoundUpToAlignment (notehdr.desc_len, 4); + + lldb::offset_t offset_next_note = offset + notehdr.name_len + notehdr.desc_len; + + // 16 bytes is UUID|MD5, 20 bytes is SHA1 + if ((notehdr.type == g_gnu_build_id) && (notehdr.name_len == 4) && + (notehdr.desc_len == 16 || notehdr.desc_len == 20)) + { + char name[4]; + if (data.GetU8 (&offset, name, 4) == NULL) + return false; + if (!strcmp(name, "GNU")) + { + uint8_t uuidbuf[20]; + if (data.GetU8 (&offset, &uuidbuf, notehdr.desc_len) == NULL) + return false; + uuid.SetBytes (uuidbuf, notehdr.desc_len); + return true; + } + } + offset = offset_next_note; + } + return false; +} + SectionList * ObjectFileELF::GetSectionList() { @@ -685,7 +779,17 @@ ObjectFileELF::GetSectionList() else if (name == g_sect_name_dwarf_debug_ranges) sect_type = eSectionTypeDWARFDebugRanges; else if (name == g_sect_name_dwarf_debug_str) sect_type = eSectionTypeDWARFDebugStr; else if (name == g_sect_name_eh_frame) sect_type = eSectionTypeEHFrame; - + else if (header.sh_type == SHT_NOTE) + { + if (!m_uuid.IsValid()) + { + DataExtractor data; + if (vm_size && (GetData (header.sh_offset, vm_size, data) == vm_size)) + { + ParseNoteGNUBuildID (data, m_uuid); + } + } + } SectionSP section_sp(new Section( GetModule(), // Module to which this section belongs. @@ -1301,16 +1405,18 @@ ObjectFileELF::DumpELFProgramHeader(Stream *s, const ELFProgramHeader &ph) void ObjectFileELF::DumpELFProgramHeader_p_type(Stream *s, elf_word p_type) { - const int kStrWidth = 10; + const int kStrWidth = 15; switch (p_type) { - CASE_AND_STREAM(s, PT_NULL , kStrWidth); - CASE_AND_STREAM(s, PT_LOAD , kStrWidth); - CASE_AND_STREAM(s, PT_DYNAMIC , kStrWidth); - CASE_AND_STREAM(s, PT_INTERP , kStrWidth); - CASE_AND_STREAM(s, PT_NOTE , kStrWidth); - CASE_AND_STREAM(s, PT_SHLIB , kStrWidth); - CASE_AND_STREAM(s, PT_PHDR , kStrWidth); + CASE_AND_STREAM(s, PT_NULL , kStrWidth); + CASE_AND_STREAM(s, PT_LOAD , kStrWidth); + CASE_AND_STREAM(s, PT_DYNAMIC , kStrWidth); + CASE_AND_STREAM(s, PT_INTERP , kStrWidth); + CASE_AND_STREAM(s, PT_NOTE , kStrWidth); + CASE_AND_STREAM(s, PT_SHLIB , kStrWidth); + CASE_AND_STREAM(s, PT_PHDR , kStrWidth); + CASE_AND_STREAM(s, PT_TLS , kStrWidth); + CASE_AND_STREAM(s, PT_GNU_EH_FRAME, kStrWidth); default: s->Printf("0x%8.8x%*s", p_type, kStrWidth - 10, ""); break; @@ -1344,9 +1450,9 @@ ObjectFileELF::DumpELFProgramHeaders(Stream *s) if (ParseProgramHeaders()) { s->PutCString("Program Headers\n"); - s->PutCString("IDX p_type p_offset p_vaddr p_paddr " + s->PutCString("IDX p_type p_offset p_vaddr p_paddr " "p_filesz p_memsz p_flags p_align\n"); - s->PutCString("==== ---------- -------- -------- -------- " + s->PutCString("==== --------------- -------- -------- -------- " "-------- -------- ------------------------- --------\n"); uint32_t idx = 0; diff --git a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h index cdf2731..f1fea3c 100644 --- a/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h +++ b/lldb/source/Plugins/ObjectFile/ELF/ObjectFileELF.h @@ -16,6 +16,7 @@ #include "lldb/lldb-private.h" #include "lldb/Host/FileSpec.h" #include "lldb/Symbol/ObjectFile.h" +#include "lldb/Core/UUID.h" #include "ELFHeader.h" @@ -65,6 +66,12 @@ public: lldb::offset_t file_offset, lldb::offset_t length, lldb_private::ModuleSpecList &specs); + + static bool + MagicBytesMatch (lldb::DataBufferSP& data_sp, + lldb::addr_t offset, + lldb::addr_t length); + //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ @@ -148,6 +155,9 @@ private: /// ELF file header. elf::ELFHeader m_header; + /// ELF build ID + lldb_private::UUID m_uuid; + /// Collection of program headers. ProgramHeaderColl m_program_headers; diff --git a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp index 140272e..9f27c1c 100644 --- a/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp +++ b/lldb/source/Plugins/ObjectFile/Mach-O/ObjectFileMachO.cpp @@ -994,8 +994,9 @@ ObjectFileMachO::ParseSections () lc_segment_name, load_cmd.fileoff, m_length); - m_sections_ap->Clear(); - return 0; + + load_cmd.fileoff = 0; + load_cmd.filesize = 0; } if (load_cmd.fileoff + load_cmd.filesize > m_length) @@ -1006,13 +1007,14 @@ ObjectFileMachO::ParseSections () // is null out the SectionList vector and if a process has been set up, dump a message // to stdout. The most common case here is core file debugging with a truncated file. const char *lc_segment_name = load_cmd.cmd == LoadCommandSegment64 ? "LC_SEGMENT_64" : "LC_SEGMENT"; - GetModule()->ReportError("is a corrupt mach-o file: load command %u %s has a fileoff + filesize (0x%" PRIx64 ") that extends beyond the end of the file (0x%" PRIx64 ")", + GetModule()->ReportError("is a corrupt mach-o file: load command %u %s has a fileoff + filesize (0x%" PRIx64 ") that extends beyond the end of the file (0x%" PRIx64 "), the segment will be truncated", i, lc_segment_name, load_cmd.fileoff + load_cmd.filesize, m_length); - m_sections_ap->Clear(); - return 0; + + // Tuncase the length + load_cmd.filesize = m_length - load_cmd.fileoff; } } if (m_data.GetU32(&offset, &load_cmd.maxprot, 4)) diff --git a/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp b/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp index 67b5c4f..4cfe38e 100644 --- a/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp +++ b/lldb/source/Plugins/OperatingSystem/Python/OperatingSystemPython.cpp @@ -21,6 +21,7 @@ #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/RegisterValue.h" +#include "lldb/Core/StreamString.h" #include "lldb/Core/ValueObjectVariable.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/PythonDataObjects.h" @@ -134,7 +135,7 @@ OperatingSystemPython::GetDynamicRegisterInfo () { if (!m_interpreter || !m_python_object_sp) return NULL; - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OS)); if (log) log->Printf ("OperatingSystemPython::GetDynamicRegisterInfo() fetching thread register definitions from python for pid %" PRIu64, m_process->GetID()); @@ -173,7 +174,7 @@ OperatingSystemPython::UpdateThreadList (ThreadList &old_thread_list, if (!m_interpreter || !m_python_object_sp) return false; - Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_PROCESS)); + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_OS)); // First thing we have to do is get the API lock, and the run lock. We're going to change the thread // content of the process, and we're going to use python, which requires the API lock to do it. @@ -191,6 +192,12 @@ OperatingSystemPython::UpdateThreadList (ThreadList &old_thread_list, PythonList threads_list(m_interpreter->OSPlugin_ThreadsInfo(m_python_object_sp)); if (threads_list) { + if (log) + { + StreamString strm; + threads_list.Dump(strm); + log->Printf("threads_list = %s", strm.GetString().c_str()); + } uint32_t i; const uint32_t num_threads = threads_list.GetSize(); if (num_threads > 0) @@ -200,21 +207,6 @@ OperatingSystemPython::UpdateThreadList (ThreadList &old_thread_list, PythonDictionary thread_dict(threads_list.GetItemAtIndex(i)); if (thread_dict) { - if (thread_dict.GetItemForKey("core")) - { - // We have some threads that are saying they are on a "core", which means - // they map the threads that are gotten from the lldb_private::Process subclass - // so clear the new threads list so the core threads don't show up - new_thread_list.Clear(); - break; - } - } - } - for (i=0; i<num_threads; ++i) - { - PythonDictionary thread_dict(threads_list.GetItemAtIndex(i)); - if (thread_dict) - { ThreadSP thread_sp (CreateThreadFromThreadInfo (thread_dict, core_thread_list, old_thread_list, NULL)); if (thread_sp) new_thread_list.AddThread(thread_sp); @@ -223,8 +215,10 @@ OperatingSystemPython::UpdateThreadList (ThreadList &old_thread_list, } } + // No new threads added from the thread info array gotten from python, just + // display the core threads. if (new_thread_list.GetSize(false) == 0) - new_thread_list = old_thread_list; + new_thread_list = core_thread_list; return new_thread_list.GetSize(false) > 0; } diff --git a/lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp b/lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp index ef4bd41..4bf4cf8 100644 --- a/lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp +++ b/lldb/source/Plugins/Platform/Linux/PlatformLinux.cpp @@ -208,6 +208,29 @@ PlatformLinux::ResolveExecutable (const FileSpec &exe_file, NULL, NULL, NULL); + if (error.Fail()) + { + // If we failed, it may be because the vendor and os aren't known. If that is the + // case, try setting them to the host architecture and give it another try. + llvm::Triple &module_triple = module_spec.GetArchitecture().GetTriple(); + bool is_vendor_specified = (module_triple.getVendor() != llvm::Triple::UnknownVendor); + bool is_os_specified = (module_triple.getOS() != llvm::Triple::UnknownOS); + if (!is_vendor_specified || !is_os_specified) + { + const llvm::Triple &host_triple = Host::GetArchitecture (Host::eSystemDefaultArchitecture).GetTriple(); + + if (!is_vendor_specified) + module_triple.setVendorName (host_triple.getVendorName()); + if (!is_os_specified) + module_triple.setOSName (host_triple.getOSName()); + + error = ModuleList::GetSharedModule (module_spec, + exe_module_sp, + NULL, + NULL, + NULL); + } + } // TODO find out why exe_module_sp might be NULL if (!exe_module_sp || exe_module_sp->GetObjectFile() == NULL) @@ -336,12 +359,14 @@ PlatformLinux::GetStatus (Stream &strm) { struct utsname un; - if (uname(&un)) { - strm << "Linux"; + Platform::GetStatus(strm); + + if (uname(&un)) return; - } - strm << un.sysname << ' ' << un.release << ' ' << un.version << '\n'; + strm.Printf (" Kernel: %s\n", un.sysname); + strm.Printf (" Release: %s\n", un.release); + strm.Printf (" Version: %s\n", un.version); } size_t diff --git a/lldb/source/Plugins/Process/CMakeLists.txt b/lldb/source/Plugins/Process/CMakeLists.txt index 274f4db..cf12bcd 100644 --- a/lldb/source/Plugins/Process/CMakeLists.txt +++ b/lldb/source/Plugins/Process/CMakeLists.txt @@ -2,7 +2,7 @@ if (CMAKE_SYSTEM_NAME MATCHES "Linux") add_subdirectory(Linux)
add_subdirectory(POSIX)
elseif (CMAKE_SYSTEM_NAME MATCHES "FreeBSD")
- #add_subdirectory(FreeBSD)
+ add_subdirectory(FreeBSD)
add_subdirectory(POSIX)
elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin")
add_subdirectory(MacOSX-Kernel)
diff --git a/lldb/source/Plugins/Process/FreeBSD/CMakeLists.txt b/lldb/source/Plugins/Process/FreeBSD/CMakeLists.txt index 430f01e..6ed7167 100644 --- a/lldb/source/Plugins/Process/FreeBSD/CMakeLists.txt +++ b/lldb/source/Plugins/Process/FreeBSD/CMakeLists.txt @@ -1,5 +1,8 @@ set(LLVM_NO_RTTI 1) +include_directories(.) +include_directories(../POSIX) + add_lldb_library(lldbPluginProcessFreeBSD ProcessFreeBSD.cpp ProcessMonitor.cpp diff --git a/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp b/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp index 18be912..cc412ed 100644 --- a/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp +++ b/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.cpp @@ -1551,7 +1551,7 @@ ProcessMonitor::GetEventMessage(lldb::tid_t tid, unsigned long *message) } Error -ProcessMonitor::Detach() +ProcessMonitor::Detach(lldb::tid_t tid) { Error result; DetachOperation op(result); diff --git a/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.h b/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.h index 7ddf083..a48cf78 100644 --- a/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.h +++ b/lldb/source/Plugins/Process/FreeBSD/ProcessMonitor.h @@ -188,7 +188,7 @@ public: BringProcessIntoLimbo(); lldb_private::Error - Detach(); + Detach(lldb::tid_t tid); private: diff --git a/lldb/source/Plugins/Process/Linux/ProcessLinux.cpp b/lldb/source/Plugins/Process/Linux/ProcessLinux.cpp index 5d93502..8c0a2a2 100644 --- a/lldb/source/Plugins/Process/Linux/ProcessLinux.cpp +++ b/lldb/source/Plugins/Process/Linux/ProcessLinux.cpp @@ -64,7 +64,7 @@ ProcessLinux::Initialize() // Constructors and destructors. ProcessLinux::ProcessLinux(Target& target, Listener &listener) - : ProcessPOSIX(target, listener) + : ProcessPOSIX(target, listener), m_stopping_threads(false) { #if 0 // FIXME: Putting this code in the ctor and saving the byte order in a @@ -134,3 +134,39 @@ ProcessLinux::EnablePluginLogging(Stream *strm, Args &command) { return NULL; } + +// ProcessPOSIX override +void +ProcessLinux::StopAllThreads(lldb::tid_t stop_tid) +{ + // If a breakpoint occurs while we're stopping threads, we'll get back + // here, but we don't want to do it again. Only the MonitorChildProcess + // thread calls this function, so we don't need to protect this flag. + if (m_stopping_threads) + return; + m_stopping_threads = true; + + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + if (log) + log->Printf ("ProcessLinux::%s() stopping all threads", __FUNCTION__); + + // Walk the thread list and stop the other threads. The thread that caused + // the stop should already be marked as stopped before we get here. + Mutex::Locker thread_list_lock(m_thread_list.GetMutex()); + + uint32_t thread_count = m_thread_list.GetSize(false); + for (uint32_t i = 0; i < thread_count; ++i) + { + POSIXThread *thread = static_cast<POSIXThread*>( + m_thread_list.GetThreadAtIndex(i, false).get()); + assert(thread); + lldb::tid_t tid = thread->GetID(); + if (!StateIsStoppedState(thread->GetState(), false)) + m_monitor->StopThread(tid); + } + + m_stopping_threads = false; + + if (log) + log->Printf ("ProcessLinux::%s() finished", __FUNCTION__); +} diff --git a/lldb/source/Plugins/Process/Linux/ProcessLinux.h b/lldb/source/Plugins/Process/Linux/ProcessLinux.h index e65f1fa..d7f338f 100644 --- a/lldb/source/Plugins/Process/Linux/ProcessLinux.h +++ b/lldb/source/Plugins/Process/Linux/ProcessLinux.h @@ -75,11 +75,28 @@ public: EnablePluginLogging(lldb_private::Stream *strm, lldb_private::Args &command); + //------------------------------------------------------------------ + // Plug-in process overrides + //------------------------------------------------------------------ + virtual lldb_private::UnixSignals & + GetUnixSignals () + { + return m_linux_signals; + } + + //------------------------------------------------------------------ + // ProcessPOSIX overrides + //------------------------------------------------------------------ + virtual void + StopAllThreads(lldb::tid_t stop_tid); + private: /// Linux-specific signal set. LinuxSignals m_linux_signals; + // Flag to avoid recursion when stopping all threads. + bool m_stopping_threads; }; #endif // liblldb_MacOSXProcess_H_ diff --git a/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp b/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp index f7f7d8c..239c959 100644 --- a/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp +++ b/lldb/source/Plugins/Process/Linux/ProcessMonitor.cpp @@ -17,6 +17,7 @@ #include <unistd.h> #include <sys/ptrace.h> #include <sys/socket.h> +#include <sys/syscall.h> #include <sys/types.h> #include <sys/wait.h> @@ -52,6 +53,10 @@ #define TRAP_HWBKPT 4 #endif +// Try to define a macro to encapsulate the tgkill syscall +// fall back on kill() if tgkill isn't available +#define tgkill(pid, tid, sig) syscall(SYS_tgkill, pid, tid, sig) + using namespace lldb_private; // FIXME: this code is host-dependent with respect to types and @@ -724,7 +729,13 @@ ResumeOperation::Execute(ProcessMonitor *monitor) data = m_signo; if (PTRACE(PTRACE_CONT, m_tid, NULL, (void*)data, 0)) + { + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + + if (log) + log->Printf ("ResumeOperation (%" PRIu64 ") failed: %s", m_tid, strerror(errno)); m_result = false; + } else m_result = true; } @@ -846,22 +857,20 @@ KillOperation::Execute(ProcessMonitor *monitor) class DetachOperation : public Operation { public: - DetachOperation(Error &result) : m_error(result) { } + DetachOperation(lldb::tid_t tid, Error &result) : m_tid(tid), m_error(result) { } void Execute(ProcessMonitor *monitor); private: + lldb::tid_t m_tid; Error &m_error; }; void DetachOperation::Execute(ProcessMonitor *monitor) { - lldb::pid_t pid = monitor->GetPID(); - - if (ptrace(PT_DETACH, pid, NULL, 0) < 0) + if (ptrace(PT_DETACH, m_tid, NULL, 0) < 0) m_error.SetErrorToErrno(); - } ProcessMonitor::OperationArgs::OperationArgs(ProcessMonitor *monitor) @@ -1087,7 +1096,6 @@ ProcessMonitor::Launch(LaunchArgs *args) const size_t err_len = 1024; char err_str[err_len]; lldb::pid_t pid; - long ptrace_opts = 0; lldb::ThreadSP inferior; Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); @@ -1203,14 +1211,7 @@ ProcessMonitor::Launch(LaunchArgs *args) assert(WIFSTOPPED(status) && wpid == pid && "Could not sync with inferior process."); - // Have the child raise an event on exit. This is used to keep the child in - // limbo until it is destroyed. - ptrace_opts |= PTRACE_O_TRACEEXIT; - - // Have the tracer trace threads which spawn in the inferior process. - ptrace_opts |= PTRACE_O_TRACEFORK | PTRACE_O_TRACEVFORK | PTRACE_O_TRACECLONE; - - if (PTRACE(PTRACE_SETOPTIONS, pid, NULL, (void*)ptrace_opts, 0) < 0) + if (!SetDefaultPtraceOpts(pid)) { args->m_error.SetErrorToErrno(); goto FINISH; @@ -1291,6 +1292,9 @@ ProcessMonitor::Attach(AttachArgs *args) lldb::ThreadSP inferior; Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + // Use a map to keep track of the threads which we have attached/need to attach. + Host::TidMap tids_to_attach; + if (pid <= 1) { args->m_error.SetErrorToGenericError(); @@ -1298,33 +1302,103 @@ ProcessMonitor::Attach(AttachArgs *args) goto FINISH; } - // Attach to the requested process. - if (PTRACE(PTRACE_ATTACH, pid, NULL, NULL, 0) < 0) + while (Host::FindProcessThreads(pid, tids_to_attach)) { - args->m_error.SetErrorToErrno(); - goto FINISH; + for (Host::TidMap::iterator it = tids_to_attach.begin(); + it != tids_to_attach.end(); ++it) + { + if (it->second == false) + { + lldb::tid_t tid = it->first; + + // Attach to the requested process. + // An attach will cause the thread to stop with a SIGSTOP. + if (PTRACE(PTRACE_ATTACH, tid, NULL, NULL, 0) < 0) + { + // No such thread. The thread may have exited. + // More error handling may be needed. + if (errno == ESRCH) + { + tids_to_attach.erase(it); + continue; + } + else + { + args->m_error.SetErrorToErrno(); + goto FINISH; + } + } + + int status; + // Need to use __WALL otherwise we receive an error with errno=ECHLD + // At this point we should have a thread stopped if waitpid succeeds. + if ((status = waitpid(tid, NULL, __WALL)) < 0) + { + // No such thread. The thread may have exited. + // More error handling may be needed. + if (errno == ESRCH) + { + tids_to_attach.erase(it); + continue; + } + else + { + args->m_error.SetErrorToErrno(); + goto FINISH; + } + } + + if (!SetDefaultPtraceOpts(tid)) + { + args->m_error.SetErrorToErrno(); + goto FINISH; + } + + // Update the process thread list with the attached thread. + inferior.reset(new POSIXThread(process, tid)); + if (log) + log->Printf ("ProcessMonitor::%s() adding tid = %" PRIu64, __FUNCTION__, tid); + process.GetThreadList().AddThread(inferior); + it->second = true; + } + } } - int status; - if ((status = waitpid(pid, NULL, 0)) < 0) + if (tids_to_attach.size() > 0) { - args->m_error.SetErrorToErrno(); - goto FINISH; + monitor->m_pid = pid; + // Let our process instance know the thread has stopped. + process.SendMessage(ProcessMessage::Trace(pid)); + } + else + { + args->m_error.SetErrorToGenericError(); + args->m_error.SetErrorString("No such process."); } - monitor->m_pid = pid; + FINISH: + return args->m_error.Success(); +} - // Update the process thread list with the attached thread. - inferior.reset(new POSIXThread(process, pid)); - if (log) - log->Printf ("ProcessMonitor::%s() adding tid = %" PRIu64, __FUNCTION__, pid); - process.GetThreadList().AddThread(inferior); +bool +ProcessMonitor::SetDefaultPtraceOpts(lldb::pid_t pid) +{ + long ptrace_opts = 0; - // Let our process instance know the thread has stopped. - process.SendMessage(ProcessMessage::Trace(pid)); + // Have the child raise an event on exit. This is used to keep the child in + // limbo until it is destroyed. + ptrace_opts |= PTRACE_O_TRACEEXIT; - FINISH: - return args->m_error.Success(); + // Have the tracer trace threads which spawn in the inferior process. + // TODO: if we want to support tracing the inferiors' child, add the + // appropriate ptrace flags here (PTRACE_O_TRACEFORK, PTRACE_O_TRACEVFORK) + ptrace_opts |= PTRACE_O_TRACECLONE; + + // Have the tracer notify us before execve returns + // (needed to disable legacy SIGTRAP generation) + ptrace_opts |= PTRACE_O_TRACEEXEC; + + return PTRACE(PTRACE_SETOPTIONS, pid, NULL, (void*)ptrace_opts, 0) >= 0; } bool @@ -1342,8 +1416,21 @@ ProcessMonitor::MonitorCallback(void *callback_baton, siginfo_t info; int ptrace_err; + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + + if (exited) + { + if (log) + log->Printf ("ProcessMonitor::%s() got exit signal, tid = %" PRIu64, __FUNCTION__, pid); + message = ProcessMessage::Exit(pid, status); + process->SendMessage(message); + return pid == process->GetID(); + } + if (!monitor->GetSignalInfo(pid, &info, ptrace_err)) { if (ptrace_err == EINVAL) { + if (log) + log->Printf ("ProcessMonitor::%s() resuming from group-stop", __FUNCTION__); // inferior process is in 'group-stop', so deliver SIGSTOP signal if (!monitor->Resume(pid, SIGSTOP)) { assert(0 && "SIGSTOP delivery failed while in 'group-stop' state"); @@ -1352,8 +1439,11 @@ ProcessMonitor::MonitorCallback(void *callback_baton, } else { // ptrace(GETSIGINFO) failed (but not due to group-stop). Most likely, // this means the child pid is gone (or not being debugged) therefore - // stop the monitor thread. - stop_monitoring = true; + // stop the monitor thread if this is the main pid. + if (log) + log->Printf ("ProcessMonitor::%s() GetSignalInfo failed: %s, tid = %" PRIu64 ", signal = %d, status = %d", + __FUNCTION__, strerror(ptrace_err), pid, signal, status); + stop_monitoring = pid == monitor->m_process->GetID(); } } else { @@ -1369,7 +1459,7 @@ ProcessMonitor::MonitorCallback(void *callback_baton, } process->SendMessage(message); - stop_monitoring = !process->IsAlive(); + stop_monitoring = false; } return stop_monitoring; @@ -1381,6 +1471,8 @@ ProcessMonitor::MonitorSIGTRAP(ProcessMonitor *monitor, { ProcessMessage message; + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + assert(monitor); assert(info && info->si_signo == SIGTRAP && "Unexpected child signal!"); @@ -1390,10 +1482,16 @@ ProcessMonitor::MonitorSIGTRAP(ProcessMonitor *monitor, assert(false && "Unexpected SIGTRAP code!"); break; - case (SIGTRAP | (PTRACE_EVENT_FORK << 8)): - case (SIGTRAP | (PTRACE_EVENT_VFORK << 8)): + // TODO: these two cases are required if we want to support tracing + // of the inferiors' children + // case (SIGTRAP | (PTRACE_EVENT_FORK << 8)): + // case (SIGTRAP | (PTRACE_EVENT_VFORK << 8)): + case (SIGTRAP | (PTRACE_EVENT_CLONE << 8)): { + if (log) + log->Printf ("ProcessMonitor::%s() received thread creation event, code = %d", __FUNCTION__, info->si_code ^ SIGTRAP); + unsigned long tid = 0; if (!monitor->GetEventMessage(pid, &tid)) tid = -1; @@ -1401,29 +1499,42 @@ ProcessMonitor::MonitorSIGTRAP(ProcessMonitor *monitor, break; } + case (SIGTRAP | (PTRACE_EVENT_EXEC << 8)): + // Don't follow the child by default and resume + monitor->Resume(pid, SIGCONT); + break; + case (SIGTRAP | (PTRACE_EVENT_EXIT << 8)): { - // The inferior process is about to exit. Maintain the process in a - // state of "limbo" until we are explicitly commanded to detach, - // destroy, resume, etc. + // The inferior process or one of its threads is about to exit. + // Maintain the process or thread in a state of "limbo" until we are + // explicitly commanded to detach, destroy, resume, etc. unsigned long data = 0; if (!monitor->GetEventMessage(pid, &data)) data = -1; + if (log) + log->Printf ("ProcessMonitor::%s() received exit event, data = %lx, pid = %" PRIu64, __FUNCTION__, data, pid); message = ProcessMessage::Limbo(pid, (data >> 8)); break; } case 0: case TRAP_TRACE: + if (log) + log->Printf ("ProcessMonitor::%s() received trace event, pid = %" PRIu64, __FUNCTION__, pid); message = ProcessMessage::Trace(pid); break; case SI_KERNEL: case TRAP_BRKPT: + if (log) + log->Printf ("ProcessMonitor::%s() received breakpoint event, pid = %" PRIu64, __FUNCTION__, pid); message = ProcessMessage::Break(pid); break; case TRAP_HWBKPT: + if (log) + log->Printf ("ProcessMonitor::%s() received watchpoint event, pid = %" PRIu64, __FUNCTION__, pid); message = ProcessMessage::Watch(pid, (lldb::addr_t)info->si_addr); break; } @@ -1438,6 +1549,8 @@ ProcessMonitor::MonitorSignal(ProcessMonitor *monitor, ProcessMessage message; int signo = info->si_signo; + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + // POSIX says that process behaviour is undefined after it ignores a SIGFPE, // SIGILL, SIGSEGV, or SIGBUS *unless* that signal was generated by a // kill(2) or raise(3). Similarly for tgkill(2) on Linux. @@ -1448,12 +1561,22 @@ ProcessMonitor::MonitorSignal(ProcessMonitor *monitor, // Similarly, ACK signals generated by this monitor. if (info->si_code == SI_TKILL || info->si_code == SI_USER) { + if (log) + log->Printf ("ProcessMonitor::%s() received signal %s with code %s, pid = %d", + __FUNCTION__, + monitor->m_process->GetUnixSignals().GetSignalAsCString (signo), + (info->si_code == SI_TKILL ? "SI_TKILL" : "SI_USER"), + info->si_pid); + if (info->si_pid == getpid()) return ProcessMessage::SignalDelivered(pid, signo); else return ProcessMessage::Signal(pid, signo); } + if (log) + log->Printf ("ProcessMonitor::%s() received signal %s", __FUNCTION__, monitor->m_process->GetUnixSignals().GetSignalAsCString (signo)); + if (signo == SIGSEGV) { lldb::addr_t fault_addr = reinterpret_cast<lldb::addr_t>(info->si_addr); ProcessMessage::CrashReason reason = GetCrashReasonForSIGSEGV(info); @@ -1483,6 +1606,144 @@ ProcessMonitor::MonitorSignal(ProcessMonitor *monitor, return ProcessMessage::Signal(pid, signo); } +bool +ProcessMonitor::StopThread(lldb::tid_t tid) +{ + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + + // FIXME: Try to use tgkill or tkill + int ret = tgkill(m_pid, tid, SIGSTOP); + if (log) + log->Printf ("ProcessMonitor::%s(bp) stopping thread, tid = %" PRIu64 ", ret = %d", __FUNCTION__, tid, ret); + + // This can happen if a thread exited while we were trying to stop it. That's OK. + // We'll get the signal for that later. + if (ret < 0) + return false; + + // Wait for the thread to stop + while (true) + { + int status = -1; + if (log) + log->Printf ("ProcessMonitor::%s(bp) waitpid...", __FUNCTION__); + lldb::pid_t wait_pid = ::waitpid (-1*m_pid, &status, __WALL); + if (log) + log->Printf ("ProcessMonitor::%s(bp) waitpid, pid = %" PRIu64 ", status = %d", __FUNCTION__, wait_pid, status); + + if (wait_pid == -1) + { + // If we got interrupted by a signal (in our process, not the + // inferior) try again. + if (errno == EINTR) + continue; + else + return false; // This is bad, but there's nothing we can do. + } + + // If this is a thread exit, we won't get any more information. + if (WIFEXITED(status)) + { + m_process->SendMessage(ProcessMessage::Exit(wait_pid, WEXITSTATUS(status))); + if (wait_pid == tid) + return true; + continue; + } + + siginfo_t info; + int ptrace_err; + if (!GetSignalInfo(wait_pid, &info, ptrace_err)) + { + if (log) + { + log->Printf ("ProcessMonitor::%s() GetSignalInfo failed.", __FUNCTION__); + + // This would be a particularly interesting case + if (ptrace_err == EINVAL) + log->Printf ("ProcessMonitor::%s() in group-stop", __FUNCTION__); + } + return false; + } + + // Handle events from other threads + if (log) + log->Printf ("ProcessMonitor::%s(bp) handling event, tid == %" PRIu64, __FUNCTION__, wait_pid); + + ProcessMessage message; + if (info.si_signo == SIGTRAP) + message = MonitorSIGTRAP(this, &info, wait_pid); + else + message = MonitorSignal(this, &info, wait_pid); + + POSIXThread *thread = static_cast<POSIXThread*>(m_process->GetThreadList().FindThreadByID(wait_pid).get()); + + // When a new thread is created, we may get a SIGSTOP for the new thread + // just before we get the SIGTRAP that we use to add the thread to our + // process thread list. We don't need to worry about that signal here. + assert(thread || message.GetKind() == ProcessMessage::eSignalMessage); + + if (!thread) + { + m_process->SendMessage(message); + continue; + } + + switch (message.GetKind()) + { + case ProcessMessage::eInvalidMessage: + break; + + // These need special handling because we don't want to send a + // resume even if we already sent a SIGSTOP to this thread. In + // this case the resume will cause the thread to disappear. It is + // unlikely that we'll ever get eExitMessage here, but the same + // reasoning applies. + case ProcessMessage::eLimboMessage: + case ProcessMessage::eExitMessage: + if (log) + log->Printf ("ProcessMonitor::%s(bp) handling message", __FUNCTION__); + // SendMessage will set the thread state as needed. + m_process->SendMessage(message); + // If this is the thread we're waiting for, stop waiting. Even + // though this wasn't the signal we expected, it's the last + // signal we'll see while this thread is alive. + if (wait_pid == tid) + return true; + break; + + case ProcessMessage::eSignalDeliveredMessage: + // This is the stop we're expecting. + if (wait_pid == tid && WIFSTOPPED(status) && WSTOPSIG(status) == SIGSTOP && info.si_code == SI_TKILL) + { + if (log) + log->Printf ("ProcessMonitor::%s(bp) received signal, done waiting", __FUNCTION__); + thread->SetState(lldb::eStateStopped); + return true; + } + // else fall-through + case ProcessMessage::eSignalMessage: + case ProcessMessage::eBreakpointMessage: + case ProcessMessage::eTraceMessage: + case ProcessMessage::eWatchpointMessage: + case ProcessMessage::eCrashMessage: + case ProcessMessage::eNewThreadMessage: + if (log) + log->Printf ("ProcessMonitor::%s(bp) handling message", __FUNCTION__); + // SendMessage will set the thread state as needed. + m_process->SendMessage(message); + // This isn't the stop we were expecting, but the thread is + // stopped. SendMessage will handle processing of this event, + // but we need to resume here to get the stop we are waiting + // for (otherwise the thread will stop again immediately when + // we try to resume). + if (wait_pid == tid) + Resume(wait_pid, eResumeSignalNone); + break; + } + } + return false; +} + ProcessMessage::CrashReason ProcessMonitor::GetCrashReasonForSIGSEGV(const siginfo_t *info) { @@ -1794,8 +2055,15 @@ bool ProcessMonitor::Resume(lldb::tid_t tid, uint32_t signo) { bool result; + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); + + if (log) + log->Printf ("ProcessMonitor::%s() resuming thread = %" PRIu64 " with signal %s", __FUNCTION__, tid, + m_process->GetUnixSignals().GetSignalAsCString (signo)); ResumeOperation op(tid, signo, result); DoOperation(&op); + if (log) + log->Printf ("ProcessMonitor::%s() resuming result = %s", __FUNCTION__, result ? "true" : "false"); return result; } @@ -1836,11 +2104,12 @@ ProcessMonitor::GetEventMessage(lldb::tid_t tid, unsigned long *message) } lldb_private::Error -ProcessMonitor::Detach() +ProcessMonitor::Detach(lldb::tid_t tid) { lldb_private::Error error; - if (m_pid != LLDB_INVALID_PROCESS_ID) { - DetachOperation op(error); + if (tid != LLDB_INVALID_THREAD_ID) + { + DetachOperation op(tid, error); DoOperation(&op); } return error; diff --git a/lldb/source/Plugins/Process/Linux/ProcessMonitor.h b/lldb/source/Plugins/Process/Linux/ProcessMonitor.h index 5563991..dfb4601 100644 --- a/lldb/source/Plugins/Process/Linux/ProcessMonitor.h +++ b/lldb/source/Plugins/Process/Linux/ProcessMonitor.h @@ -63,6 +63,11 @@ public: ~ProcessMonitor(); + enum ResumeSignals + { + eResumeSignalNone = 0 + }; + /// Provides the process number of debugee. lldb::pid_t GetPID() const { return m_pid; } @@ -170,8 +175,11 @@ public: BringProcessIntoLimbo(); lldb_private::Error - Detach(); + Detach(lldb::tid_t tid); + /// Stops the requested thread and waits for the stop signal. + bool + StopThread(lldb::tid_t tid); private: ProcessLinux *m_process; @@ -254,6 +262,9 @@ private: static bool Attach(AttachArgs *args); + static bool + SetDefaultPtraceOpts(const lldb::pid_t); + static void ServeOperation(OperationArgs *args); diff --git a/lldb/source/Plugins/Process/POSIX/POSIXThread.cpp b/lldb/source/Plugins/Process/POSIX/POSIXThread.cpp index e08414f..0f86f30 100644 --- a/lldb/source/Plugins/Process/POSIX/POSIXThread.cpp +++ b/lldb/source/Plugins/Process/POSIX/POSIXThread.cpp @@ -17,6 +17,7 @@ // Project includes #include "lldb/Breakpoint/Watchpoint.h" #include "lldb/Core/Debugger.h" +#include "lldb/Core/State.h" #include "lldb/Host/Host.h" #include "lldb/Target/Process.h" #include "lldb/Target/StopInfo.h" @@ -45,6 +46,20 @@ POSIXThread::POSIXThread(Process &process, lldb::tid_t tid) Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) log->Printf ("POSIXThread::%s (tid = %" PRIi64 ")", __FUNCTION__, tid); + + // Set the current watchpoints for this thread. + Target &target = GetProcess()->GetTarget(); + const WatchpointList &wp_list = target.GetWatchpointList(); + size_t wp_size = wp_list.GetSize(); + + for (uint32_t wp_idx = 0; wp_idx < wp_size; wp_idx++) + { + lldb::WatchpointSP wp = wp_list.GetByIndex(wp_idx); + if (wp.get() && wp->IsEnabled()) + { + assert(EnableHardwareWatchpoint(wp.get())); + } + } } POSIXThread::~POSIXThread() @@ -63,15 +78,24 @@ POSIXThread::GetMonitor() void POSIXThread::RefreshStateAfterStop() { + // Invalidate all registers in our register context. We don't set "force" to + // true because the stop reply packet might have had some register values + // that were expedited and these will already be copied into the register + // context by the time this function gets called. The KDPRegisterContext + // class has been made smart enough to detect when it needs to invalidate + // which registers are valid by putting hooks in the register read and + // register supply functions where they check the process stop ID and do + // the right thing. + //if (StateIsStoppedState(GetState()) + { + const bool force = false; + GetRegisterContext()->InvalidateIfNeeded (force); + } + // FIXME: This should probably happen somewhere else. + SetResumeState(eStateRunning); Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); - if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) - log->Printf ("POSIXThread::%s ()", __FUNCTION__); - - // Let all threads recover from stopping and do any clean up based - // on the previous thread state (if any). - ProcessSP base = GetProcess(); - ProcessPOSIX &process = static_cast<ProcessPOSIX&>(*base); - process.GetThreadList().RefreshStateAfterStop(); + if (log) + log->Printf ("POSIXThread::%s (tid = %" PRIi64 ") setting thread resume state to running", __FUNCTION__, GetID()); } const char * @@ -157,11 +181,20 @@ POSIXThread::GetUnwinder() void POSIXThread::WillResume(lldb::StateType resume_state) { - // TODO: the line below shouldn't really be done, but + Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); + if (log) + log->Printf ("POSIXThread::%s (tid = %" PRIi64 ") setting thread resume state to %s", __FUNCTION__, GetID(), StateAsCString(resume_state)); + // TODO: the line below shouldn't really be done, but // the POSIXThread might rely on this so I will leave this in for now SetResumeState(resume_state); } +void +POSIXThread::DidStop() +{ + // Don't set the thread state to stopped unless we really stopped. +} + bool POSIXThread::Resume() { @@ -170,8 +203,9 @@ POSIXThread::Resume() bool status; Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_THREAD)); - if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) - log->Printf ("POSIXThread::%s ()", __FUNCTION__); + if (log) + log->Printf ("POSIXThread::%s (), resume_state = %s", __FUNCTION__, + StateAsCString(resume_state)); switch (resume_state) { @@ -211,10 +245,14 @@ POSIXThread::Notify(const ProcessMessage &message) assert(false && "Unexpected message kind!"); break; + case ProcessMessage::eExitMessage: + // Nothing to be done. + break; + case ProcessMessage::eLimboMessage: LimboNotify(message); break; - + case ProcessMessage::eSignalMessage: SignalNotify(message); break; @@ -248,27 +286,21 @@ POSIXThread::Notify(const ProcessMessage &message) bool POSIXThread::EnableHardwareWatchpoint(Watchpoint *wp) { - bool result = false; + bool wp_set = false; if (wp) { addr_t wp_addr = wp->GetLoadAddress(); size_t wp_size = wp->GetByteSize(); bool wp_read = wp->WatchpointRead(); bool wp_write = wp->WatchpointWrite(); - uint32_t wp_hw_index; - lldb::RegisterContextSP reg_ctx_sp = GetRegisterContext(); - if (reg_ctx_sp.get()) - { - wp_hw_index = reg_ctx_sp->SetHardwareWatchpoint(wp_addr, wp_size, - wp_read, wp_write); - if (wp_hw_index != LLDB_INVALID_INDEX32) - { - wp->SetHardwareIndex(wp_hw_index); - result = true; - } - } + uint32_t wp_hw_index = wp->GetHardwareIndex(); + RegisterContextPOSIX* reg_ctx = GetRegisterContextPOSIX(); + if (reg_ctx) + wp_set = reg_ctx->SetHardwareWatchpointWithIndex(wp_addr, wp_size, + wp_read, wp_write, + wp_hw_index); } - return result; + return wp_set; } bool @@ -279,11 +311,7 @@ POSIXThread::DisableHardwareWatchpoint(Watchpoint *wp) { lldb::RegisterContextSP reg_ctx_sp = GetRegisterContext(); if (reg_ctx_sp.get()) - { result = reg_ctx_sp->ClearHardwareWatchpoint(wp->GetHardwareIndex()); - if (result == true) - wp->SetHardwareIndex(LLDB_INVALID_INDEX32); - } } return result; } @@ -297,6 +325,27 @@ POSIXThread::NumSupportedHardwareWatchpoints() return 0; } +uint32_t +POSIXThread::FindVacantWatchpointIndex() +{ + uint32_t hw_index = LLDB_INVALID_INDEX32; + uint32_t num_hw_wps = NumSupportedHardwareWatchpoints(); + uint32_t wp_idx; + RegisterContextPOSIX* reg_ctx = GetRegisterContextPOSIX(); + if (reg_ctx) + { + for (wp_idx = 0; wp_idx < num_hw_wps; wp_idx++) + { + if (reg_ctx->IsWatchpointVacant(wp_idx)) + { + hw_index = wp_idx; + break; + } + } + } + return hw_index; +} + void POSIXThread::BreakNotify(const ProcessMessage &message) { @@ -318,6 +367,9 @@ POSIXThread::BreakNotify(const ProcessMessage &message) lldb::break_id_t bp_id = bp_site->GetID(); assert(bp_site && bp_site->ValidForThisThread(this)); + // Make this thread the selected thread + GetProcess()->GetThreadList().SetSelectedThreadByID(GetID()); + m_breakpoint = bp_site; SetStopInfo (StopInfo::CreateStopReasonWithBreakpointSiteID(*this, bp_id)); } @@ -408,6 +460,7 @@ POSIXThread::SignalDeliveredNotify(const ProcessMessage &message) void POSIXThread::CrashNotify(const ProcessMessage &message) { + // FIXME: Update stop reason as per bugzilla 14598 int signo = message.GetSignal(); assert(message.GetKind() == ProcessMessage::eCrashMessage); diff --git a/lldb/source/Plugins/Process/POSIX/POSIXThread.h b/lldb/source/Plugins/Process/POSIX/POSIXThread.h index 8d353aa..251281e 100644 --- a/lldb/source/Plugins/Process/POSIX/POSIXThread.h +++ b/lldb/source/Plugins/Process/POSIX/POSIXThread.h @@ -39,6 +39,10 @@ public: virtual void WillResume(lldb::StateType resume_state); + // This notifies the thread when a private stop occurs. + virtual void + DidStop (); + const char * GetInfo(); @@ -78,6 +82,8 @@ public: uint32_t NumSupportedHardwareWatchpoints(); + uint32_t FindVacantWatchpointIndex(); + private: RegisterContextPOSIX * GetRegisterContextPOSIX () @@ -108,6 +114,7 @@ private: void SignalDeliveredNotify(const ProcessMessage &message); void CrashNotify(const ProcessMessage &message); void ThreadNotify(const ProcessMessage &message); + void ExitNotify(const ProcessMessage &message); lldb_private::Unwind * GetUnwinder(); diff --git a/lldb/source/Plugins/Process/POSIX/ProcessMessage.h b/lldb/source/Plugins/Process/POSIX/ProcessMessage.h index 2107e92..c76ca01 100644 --- a/lldb/source/Plugins/Process/POSIX/ProcessMessage.h +++ b/lldb/source/Plugins/Process/POSIX/ProcessMessage.h @@ -117,11 +117,16 @@ public: return message; } - /// Indicates that the thread @p tid was spawned. + /// Indicates that the thread @p child_tid was spawned. static ProcessMessage NewThread(lldb::tid_t parent_tid, lldb::tid_t child_tid) { return ProcessMessage(parent_tid, eNewThreadMessage, child_tid); } + /// Indicates that the thread @p tid is about to exit with status @p status. + static ProcessMessage Exit(lldb::tid_t tid, int status) { + return ProcessMessage(tid, eExitMessage, status); + } + int GetExitStatus() const { assert(GetKind() == eExitMessage || GetKind() == eLimboMessage); return m_status; diff --git a/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.cpp b/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.cpp index 471843b..32dfdd7 100644 --- a/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.cpp +++ b/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.cpp @@ -75,6 +75,7 @@ ProcessPOSIX::ProcessPOSIX(Target& target, Listener &listener) m_byte_order(lldb::endian::InlHostByteOrder()), m_monitor(NULL), m_module(NULL), + m_message_mutex (Mutex::eMutexTypeRecursive), m_in_limbo(false), m_exit_now(false) { @@ -328,7 +329,14 @@ ProcessPOSIX::DoDetach(bool keep_stopped) return error; } - error = m_monitor->Detach(); + uint32_t thread_count = m_thread_list.GetSize(false); + for (uint32_t i = 0; i < thread_count; ++i) + { + POSIXThread *thread = static_cast<POSIXThread*>( + m_thread_list.GetThreadAtIndex(i, false).get()); + error = m_monitor->Detach(thread->GetID()); + } + if (error.Success()) SetPrivateState(eStateDetached); @@ -374,31 +382,58 @@ ProcessPOSIX::SendMessage(const ProcessMessage &message) { Mutex::Locker lock(m_message_mutex); + POSIXThread *thread = static_cast<POSIXThread*>( + m_thread_list.FindThreadByID(message.GetTID(), false).get()); + switch (message.GetKind()) { case ProcessMessage::eInvalidMessage: return; case ProcessMessage::eLimboMessage: - m_in_limbo = true; - m_exit_status = message.GetExitStatus(); - if (m_exit_now) + assert(thread); + thread->SetState(eStateStopped); + if (message.GetTID() == GetID()) { - SetPrivateState(eStateExited); - m_monitor->Detach(); + m_in_limbo = true; + m_exit_status = message.GetExitStatus(); + if (m_exit_now) + { + SetPrivateState(eStateExited); + m_monitor->Detach(GetID()); + } + else + { + StopAllThreads(message.GetTID()); + SetPrivateState(eStateStopped); + } } else + { + StopAllThreads(message.GetTID()); SetPrivateState(eStateStopped); + } break; case ProcessMessage::eExitMessage: - m_exit_status = message.GetExitStatus(); - SetExitStatus(m_exit_status, NULL); + assert(thread); + thread->SetState(eStateExited); + // FIXME: I'm not sure we need to do this. + if (message.GetTID() == GetID()) + { + m_exit_status = message.GetExitStatus(); + SetExitStatus(m_exit_status, NULL); + } break; - case ProcessMessage::eTraceMessage: case ProcessMessage::eBreakpointMessage: + case ProcessMessage::eTraceMessage: case ProcessMessage::eWatchpointMessage: + case ProcessMessage::eNewThreadMessage: + case ProcessMessage::eCrashMessage: + assert(thread); + thread->SetState(eStateStopped); + StopAllThreads(message.GetTID()); SetPrivateState(eStateStopped); break; @@ -408,6 +443,9 @@ ProcessPOSIX::SendMessage(const ProcessMessage &message) lldb::tid_t tid = message.GetTID(); lldb::tid_t pid = GetID(); if (tid == pid) { + assert(thread); + thread->SetState(eStateStopped); + StopAllThreads(message.GetTID()); SetPrivateState(eStateStopped); break; } else { @@ -416,51 +454,65 @@ ProcessPOSIX::SendMessage(const ProcessMessage &message) } } - case ProcessMessage::eCrashMessage: - // FIXME: Update stop reason as per bugzilla 14598 - SetPrivateState(eStateStopped); - break; - - case ProcessMessage::eNewThreadMessage: - SetPrivateState(eStateStopped); - break; } m_message_queue.push(message); } +void +ProcessPOSIX::StopAllThreads(lldb::tid_t stop_tid) +{ + // FIXME: Will this work the same way on FreeBSD and Linux? +} + void ProcessPOSIX::RefreshStateAfterStop() { Log *log (ProcessPOSIXLog::GetLogIfAllCategoriesSet (POSIX_LOG_PROCESS)); if (log && log->GetMask().Test(POSIX_LOG_VERBOSE)) - log->Printf ("ProcessPOSIX::%s()", __FUNCTION__); + log->Printf ("ProcessPOSIX::%s(), message_queue size = %d", __FUNCTION__, (int)m_message_queue.size()); Mutex::Locker lock(m_message_mutex); - if (m_message_queue.empty()) - return; - ProcessMessage &message = m_message_queue.front(); + // This method used to only handle one message. Changing it to loop allows + // it to handle the case where we hit a breakpoint while handling a different + // breakpoint. + while (!m_message_queue.empty()) + { + ProcessMessage &message = m_message_queue.front(); + + // Resolve the thread this message corresponds to and pass it along. + lldb::tid_t tid = message.GetTID(); + if (log) + log->Printf ("ProcessPOSIX::%s(), message_queue size = %d, pid = %" PRIi64, __FUNCTION__, (int)m_message_queue.size(), tid); + POSIXThread *thread = static_cast<POSIXThread*>( + GetThreadList().FindThreadByID(tid, false).get()); - // Resolve the thread this message corresponds to and pass it along. - // FIXME: we're really dealing with the pid here. This should get - // fixed when this code is fixed to handle multiple threads. - lldb::tid_t tid = message.GetTID(); - if (log) - log->Printf ("ProcessPOSIX::%s() pid = %" PRIi64, __FUNCTION__, tid); - POSIXThread *thread = static_cast<POSIXThread*>( - GetThreadList().FindThreadByID(tid, false).get()); + if (message.GetKind() == ProcessMessage::eNewThreadMessage) + { + if (log) + log->Printf ("ProcessPOSIX::%s() adding thread, tid = %" PRIi64, __FUNCTION__, message.GetChildTID()); + ThreadSP thread_sp; + thread_sp.reset(new POSIXThread(*this, message.GetChildTID())); + m_thread_list.AddThread(thread_sp); + } - if (message.GetKind() == ProcessMessage::eNewThreadMessage) { - ThreadSP thread_sp; - thread_sp.reset(new POSIXThread(*this, message.GetChildTID())); - m_thread_list.AddThread(thread_sp); - } + m_thread_list.RefreshStateAfterStop(); - assert(thread); - thread->Notify(message); + if (thread) + thread->Notify(message); - m_message_queue.pop(); + if (message.GetKind() == ProcessMessage::eExitMessage) + { + // FIXME: We should tell the user about this, but the limbo message is probably better for that. + if (log) + log->Printf ("ProcessPOSIX::%s() removing thread, tid = %" PRIi64, __FUNCTION__, tid); + ThreadSP thread_sp = m_thread_list.RemoveThreadByID(tid, false); + thread_sp.reset(); + } + + m_message_queue.pop(); + } } bool @@ -602,31 +654,44 @@ ProcessPOSIX::EnableWatchpoint(Watchpoint *wp, bool notify) return error; } - bool wp_enabled = true; - uint32_t thread_count = m_thread_list.GetSize(false); - for (uint32_t i = 0; i < thread_count; ++i) - { - POSIXThread *thread = static_cast<POSIXThread*>( - m_thread_list.GetThreadAtIndex(i, false).get()); - if (thread) - wp_enabled &= thread->EnableHardwareWatchpoint(wp); - else - { - wp_enabled = false; - break; - } - } - if (wp_enabled) + // Try to find a vacant watchpoint slot in the inferiors' main thread + uint32_t wp_hw_index = LLDB_INVALID_INDEX32; + POSIXThread *thread = static_cast<POSIXThread*>( + m_thread_list.GetThreadAtIndex(0, false).get()); + + if (thread) + wp_hw_index = thread->FindVacantWatchpointIndex(); + + if (wp_hw_index == LLDB_INVALID_INDEX32) { - wp->SetEnabled(true, notify); - return error; + error.SetErrorString("Setting hardware watchpoint failed."); } else { - // Watchpoint enabling failed on at least one - // of the threads so roll back all of them - DisableWatchpoint(wp, false); - error.SetErrorString("Setting hardware watchpoint failed"); + wp->SetHardwareIndex(wp_hw_index); + bool wp_enabled = true; + uint32_t thread_count = m_thread_list.GetSize(false); + for (uint32_t i = 0; i < thread_count; ++i) + { + thread = static_cast<POSIXThread*>( + m_thread_list.GetThreadAtIndex(i, false).get()); + if (thread) + wp_enabled &= thread->EnableHardwareWatchpoint(wp); + else + wp_enabled = false; + } + if (wp_enabled) + { + wp->SetEnabled(true, notify); + return error; + } + else + { + // Watchpoint enabling failed on at least one + // of the threads so roll back all of them + DisableWatchpoint(wp, false); + error.SetErrorString("Setting hardware watchpoint failed"); + } } } else @@ -672,6 +737,7 @@ ProcessPOSIX::DisableWatchpoint(Watchpoint *wp, bool notify) } if (wp_disabled) { + wp->SetHardwareIndex(LLDB_INVALID_INDEX32); wp->SetEnabled(false, notify); return error; } diff --git a/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.h b/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.h index ce31d8f..b540805 100644 --- a/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.h +++ b/lldb/source/Plugins/Process/POSIX/ProcessPOSIX.h @@ -152,6 +152,11 @@ public: GetFilePath(const lldb_private::ProcessLaunchInfo::FileAction *file_action, const char *default_path); + /// Stops all threads in the process. + /// The \p stop_tid parameter indicates the thread which initiated the stop. + virtual void + StopAllThreads(lldb::tid_t stop_tid); + protected: /// Target byte order. lldb::ByteOrder m_byte_order; diff --git a/lldb/source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.cpp b/lldb/source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.cpp index bd86678..0fb9dc1 100644 --- a/lldb/source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.cpp +++ b/lldb/source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.cpp @@ -8,6 +8,7 @@ //===---------------------------------------------------------------------===// #include "RegisterContextFreeBSD_x86_64.h" +#include <vector> using namespace lldb_private; @@ -18,13 +19,13 @@ using namespace lldb_private; // Update the FreeBSD specific information (offset and size). #define UPDATE_GPR_INFO(reg) \ do { \ - m_register_infos[gpr_##reg].byte_size = sizeof(GPR::reg); \ - m_register_infos[gpr_##reg].byte_offset = GPR_OFFSET(reg); \ + GetRegisterContext()[gpr_##reg].byte_size = sizeof(GPR::reg); \ + GetRegisterContext()[gpr_##reg].byte_offset = GPR_OFFSET(reg); \ } while(false); #define UPDATE_I386_GPR_INFO(i386_reg, reg) \ do { \ - m_register_infos[gpr_##i386_reg].byte_offset = GPR_OFFSET(reg); \ + GetRegisterContext()[gpr_##i386_reg].byte_offset = GPR_OFFSET(reg); \ } while(false); typedef struct _GPR @@ -57,20 +58,18 @@ typedef struct _GPR uint64_t ss; } GPR; -RegisterInfo *RegisterContextFreeBSD_x86_64::m_register_infos = nullptr; +// Use a singleton function to avoid global constructors in shared libraries. +static std::vector<RegisterInfo> & GetRegisterContext () { + static std::vector<RegisterInfo> g_register_infos; + return g_register_infos; +} + RegisterContextFreeBSD_x86_64::RegisterContextFreeBSD_x86_64(Thread &thread, uint32_t concrete_frame_idx): RegisterContext_x86_64(thread, concrete_frame_idx) { } -RegisterContextFreeBSD_x86_64::~RegisterContextFreeBSD_x86_64() -{ - if (m_register_infos) - delete m_register_infos; - m_register_infos = nullptr; -} - size_t RegisterContextFreeBSD_x86_64::GetGPRSize() { @@ -81,19 +80,18 @@ const RegisterInfo * RegisterContextFreeBSD_x86_64::GetRegisterInfo() { // Allocate RegisterInfo only once - if (!m_register_infos) + if (GetRegisterContext().empty()) { - m_register_infos = new RegisterInfo[k_num_registers]; // Copy the register information from base class - if (m_register_infos) + const RegisterInfo *base_info = RegisterContext_x86_64::GetRegisterInfo(); + if (base_info) { - memcpy(m_register_infos, RegisterContext_x86_64::GetRegisterInfo(), - sizeof(RegisterInfo) * k_num_registers); - // Update the Linux specific register information (offset and size). + GetRegisterContext().insert(GetRegisterContext().end(), &base_info[0], &base_info[k_num_registers]); + // Update the FreeBSD specific register information (offset and size). UpdateRegisterInfo(); } } - return m_register_infos; + return &GetRegisterContext()[0]; } void diff --git a/lldb/source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.h b/lldb/source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.h index 4ea87c5..ffff40a 100644 --- a/lldb/source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.h +++ b/lldb/source/Plugins/Process/POSIX/RegisterContextFreeBSD_x86_64.h @@ -17,7 +17,6 @@ class RegisterContextFreeBSD_x86_64: { public: RegisterContextFreeBSD_x86_64(lldb_private::Thread &thread, uint32_t concrete_frame_idx); - virtual ~RegisterContextFreeBSD_x86_64(); size_t GetGPRSize(); @@ -28,9 +27,6 @@ protected: virtual void UpdateRegisterInfo(); - -private: - static lldb_private::RegisterInfo *m_register_infos; }; #endif diff --git a/lldb/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.cpp b/lldb/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.cpp index 2266d7c..9b48007 100644 --- a/lldb/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.cpp +++ b/lldb/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.cpp @@ -8,6 +8,7 @@ //===---------------------------------------------------------------------===// #include "RegisterContextLinux_x86_64.h" +#include <vector> using namespace lldb_private; @@ -18,13 +19,13 @@ using namespace lldb_private; // Update the Linux specific information (offset and size). #define UPDATE_GPR_INFO(reg) \ do { \ - m_register_infos[gpr_##reg].byte_size = sizeof(GPR::reg); \ - m_register_infos[gpr_##reg].byte_offset = GPR_OFFSET(reg); \ + GetRegisterContext()[gpr_##reg].byte_size = sizeof(GPR::reg); \ + GetRegisterContext()[gpr_##reg].byte_offset = GPR_OFFSET(reg); \ } while(false); #define UPDATE_I386_GPR_INFO(i386_reg, reg) \ do { \ - m_register_infos[gpr_##i386_reg].byte_offset = GPR_OFFSET(reg); \ + GetRegisterContext()[gpr_##i386_reg].byte_offset = GPR_OFFSET(reg); \ } while(false); #define DR_OFFSET(reg_index) \ @@ -32,8 +33,8 @@ do { \ #define UPDATE_DR_INFO(reg_index) \ do { \ - m_register_infos[dr##reg_index].byte_size = sizeof(UserArea::u_debugreg[0]); \ - m_register_infos[dr##reg_index].byte_offset = DR_OFFSET(reg_index); \ + GetRegisterContext()[dr##reg_index].byte_size = sizeof(UserArea::u_debugreg[0]); \ + GetRegisterContext()[dr##reg_index].byte_offset = DR_OFFSET(reg_index); \ } while(false); typedef struct _GPR @@ -92,20 +93,17 @@ struct UserArea uint64_t fault_address; // Control register CR3. }; -RegisterInfo *RegisterContextLinux_x86_64::m_register_infos = nullptr; +// Use a singleton function to avoid global constructors in shared libraries. +static std::vector<RegisterInfo> & GetRegisterContext () { + static std::vector<RegisterInfo> g_register_infos; + return g_register_infos; +} RegisterContextLinux_x86_64::RegisterContextLinux_x86_64(Thread &thread, uint32_t concrete_frame_idx): RegisterContext_x86_64(thread, concrete_frame_idx) { } -RegisterContextLinux_x86_64::~RegisterContextLinux_x86_64() -{ - if (m_register_infos) - delete m_register_infos; - m_register_infos = nullptr; -} - size_t RegisterContextLinux_x86_64::GetGPRSize() { @@ -116,19 +114,18 @@ const RegisterInfo * RegisterContextLinux_x86_64::GetRegisterInfo() { // Allocate RegisterInfo only once - if (!m_register_infos) + if (GetRegisterContext().empty()) { - m_register_infos = new RegisterInfo[k_num_registers]; // Copy the register information from base class - if (m_register_infos) + const RegisterInfo *base_info = RegisterContext_x86_64::GetRegisterInfo(); + if (base_info) { - memcpy(m_register_infos, RegisterContext_x86_64::GetRegisterInfo(), - sizeof(RegisterInfo) * k_num_registers); + GetRegisterContext().insert(GetRegisterContext().end(), &base_info[0], &base_info[k_num_registers]); // Update the Linux specific register information (offset and size). UpdateRegisterInfo(); } } - return m_register_infos; + return &GetRegisterContext()[0]; } void diff --git a/lldb/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.h b/lldb/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.h index d9d400d..1509ef5 100644 --- a/lldb/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.h +++ b/lldb/source/Plugins/Process/POSIX/RegisterContextLinux_x86_64.h @@ -17,7 +17,6 @@ class RegisterContextLinux_x86_64: { public: RegisterContextLinux_x86_64(lldb_private::Thread &thread, uint32_t concrete_frame_idx); - virtual ~RegisterContextLinux_x86_64(); size_t GetGPRSize(); @@ -28,9 +27,6 @@ protected: virtual void UpdateRegisterInfo(); - -private: - static lldb_private::RegisterInfo *m_register_infos; }; #endif diff --git a/lldb/source/Plugins/Process/POSIX/RegisterContextPOSIX.h b/lldb/source/Plugins/Process/POSIX/RegisterContextPOSIX.h index bd26a31..63ae01e 100644 --- a/lldb/source/Plugins/Process/POSIX/RegisterContextPOSIX.h +++ b/lldb/source/Plugins/Process/POSIX/RegisterContextPOSIX.h @@ -25,7 +25,8 @@ class RegisterContextPOSIX public: RegisterContextPOSIX(lldb_private::Thread &thread, uint32_t concrete_frame_idx) - : RegisterContext(thread, concrete_frame_idx) { } + : RegisterContext(thread, concrete_frame_idx) + { m_watchpoints_initialized = false; } /// Updates the register state of the associated thread after hitting a /// breakpoint (if that make sense for the architecture). Default @@ -52,7 +53,18 @@ public: // Returns the watchpoint address associated with a watchpoint hardware // index. virtual lldb::addr_t - GetWatchpointAddress (uint32_t hw_index) {return LLDB_INVALID_ADDRESS; } + GetWatchpointAddress (uint32_t hw_index) { return LLDB_INVALID_ADDRESS; } + + virtual bool + IsWatchpointVacant (uint32_t hw_index) { return false; } + + virtual bool + SetHardwareWatchpointWithIndex (lldb::addr_t addr, size_t size, + bool read, bool write, + uint32_t hw_index) { return false; } + +protected: + bool m_watchpoints_initialized; }; #endif // #ifndef liblldb_RegisterContextPOSIX_H_ diff --git a/lldb/source/Plugins/Process/POSIX/RegisterContext_x86_64.cpp b/lldb/source/Plugins/Process/POSIX/RegisterContext_x86_64.cpp index ec7c41f..03bccd2 100644 --- a/lldb/source/Plugins/Process/POSIX/RegisterContext_x86_64.cpp +++ b/lldb/source/Plugins/Process/POSIX/RegisterContext_x86_64.cpp @@ -1242,6 +1242,8 @@ RegisterContext_x86_64::IsWatchpointVacant(uint32_t hw_index) bool is_vacant = false; RegisterValue value; + assert(hw_index < NumSupportedHardwareWatchpoints()); + if (ReadRegister(dr7, value)) { uint64_t val = value.GetAsUInt64(); @@ -1282,23 +1284,47 @@ RegisterContext_x86_64::SetHardwareWatchpoint(addr_t addr, size_t size, bool read, bool write) { const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + uint32_t hw_index; - if (num_hw_watchpoints == 0) - return LLDB_INVALID_INDEX32; + for (hw_index = 0; hw_index < num_hw_watchpoints; ++hw_index) + { + if (IsWatchpointVacant(hw_index)) + return SetHardwareWatchpointWithIndex(addr, size, + read, write, + hw_index); + } + + return LLDB_INVALID_INDEX32; +} + +bool +RegisterContext_x86_64::SetHardwareWatchpointWithIndex(addr_t addr, size_t size, + bool read, bool write, + uint32_t hw_index) +{ + const uint32_t num_hw_watchpoints = NumSupportedHardwareWatchpoints(); + + if (num_hw_watchpoints == 0 || hw_index >= num_hw_watchpoints) + return false; if (!(size == 1 || size == 2 || size == 4 || size == 8)) - return LLDB_INVALID_INDEX32; + return false; if (read == false && write == false) - return LLDB_INVALID_INDEX32; + return false; - uint32_t hw_index = 0; - for (hw_index = 0; hw_index < num_hw_watchpoints; ++hw_index) + if (m_watchpoints_initialized == false) { - if (IsWatchpointVacant(hw_index)) - break; + // Reset the debug status and debug control registers + RegisterValue zero_bits = RegisterValue(uint64_t(0)); + if (!WriteRegister(dr6, zero_bits) || !WriteRegister(dr7, zero_bits)) + return false; + m_watchpoints_initialized = true; } + if (!IsWatchpointVacant(hw_index)) + return false; + // Set both dr7 (debug control register) and dri (debug address register). // dr7{7-0} encodes the local/gloabl enable bits: @@ -1335,11 +1361,11 @@ RegisterContext_x86_64::SetHardwareWatchpoint(addr_t addr, size_t size, if (WriteRegister(dr0 + hw_index, RegisterValue(addr)) && WriteRegister(dr7, RegisterValue(new_dr7_bits))) - return hw_index; + return true; } } - return LLDB_INVALID_INDEX32; + return false; } bool diff --git a/lldb/source/Plugins/Process/POSIX/RegisterContext_x86_64.h b/lldb/source/Plugins/Process/POSIX/RegisterContext_x86_64.h index 08ce682..9d59bd7 100644 --- a/lldb/source/Plugins/Process/POSIX/RegisterContext_x86_64.h +++ b/lldb/source/Plugins/Process/POSIX/RegisterContext_x86_64.h @@ -193,6 +193,10 @@ public: SetHardwareWatchpoint(lldb::addr_t, size_t size, bool read, bool write); bool + SetHardwareWatchpointWithIndex(lldb::addr_t, size_t size, bool read, + bool write, uint32_t hw_index); + + bool ClearHardwareWatchpoint(uint32_t hw_index); bool diff --git a/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp b/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp index 5382489..84343b92 100644 --- a/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp +++ b/lldb/source/Plugins/Process/Utility/RegisterContextLLDB.cpp @@ -1305,21 +1305,20 @@ RegisterContextLLDB::ReadGPRValue (int register_kind, uint32_t regnum, addr_t &v return false; } - bool pc_or_return_address = false; + bool pc_register = false; uint32_t generic_regnum; - if (register_kind == eRegisterKindGeneric - && (regnum == LLDB_REGNUM_GENERIC_PC || regnum == LLDB_REGNUM_GENERIC_RA)) + if (register_kind == eRegisterKindGeneric && regnum == LLDB_REGNUM_GENERIC_PC) { - pc_or_return_address = true; + pc_register = true; } else if (m_thread.GetRegisterContext()->ConvertBetweenRegisterKinds (register_kind, regnum, eRegisterKindGeneric, generic_regnum) - && (generic_regnum == LLDB_REGNUM_GENERIC_PC || generic_regnum == LLDB_REGNUM_GENERIC_RA)) + && generic_regnum == LLDB_REGNUM_GENERIC_PC) { - pc_or_return_address = true; + pc_register = true; } lldb_private::UnwindLLDB::RegisterLocation regloc; - if (!m_parent_unwind.SearchForSavedLocationForRegister (lldb_regnum, regloc, m_frame_number - 1, pc_or_return_address)) + if (!m_parent_unwind.SearchForSavedLocationForRegister (lldb_regnum, regloc, m_frame_number - 1, pc_register)) { return false; } diff --git a/lldb/source/Plugins/Process/Utility/UnwindLLDB.cpp b/lldb/source/Plugins/Process/Utility/UnwindLLDB.cpp index 9056a3f..0eea003 100644 --- a/lldb/source/Plugins/Process/Utility/UnwindLLDB.cpp +++ b/lldb/source/Plugins/Process/Utility/UnwindLLDB.cpp @@ -278,7 +278,7 @@ UnwindLLDB::GetRegisterContextForFrameNum (uint32_t frame_num) } bool -UnwindLLDB::SearchForSavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation ®loc, uint32_t starting_frame_num, bool pc_or_return_address_reg) +UnwindLLDB::SearchForSavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation ®loc, uint32_t starting_frame_num, bool pc_reg) { int64_t frame_num = starting_frame_num; if (frame_num >= m_frames.size()) @@ -286,7 +286,7 @@ UnwindLLDB::SearchForSavedLocationForRegister (uint32_t lldb_regnum, lldb_privat // Never interrogate more than one level while looking for the saved pc value. If the value // isn't saved by frame_num, none of the frames lower on the stack will have a useful value. - if (pc_or_return_address_reg) + if (pc_reg) { UnwindLLDB::RegisterSearchResult result; result = m_frames[frame_num]->reg_ctx_lldb_sp->SavedLocationForRegister (lldb_regnum, regloc); diff --git a/lldb/source/Plugins/Process/Utility/UnwindLLDB.h b/lldb/source/Plugins/Process/Utility/UnwindLLDB.h index 051e6c9..5725654 100644 --- a/lldb/source/Plugins/Process/Utility/UnwindLLDB.h +++ b/lldb/source/Plugins/Process/Utility/UnwindLLDB.h @@ -87,7 +87,7 @@ protected: // Iterate over the RegisterContextLLDB's in our m_frames vector, look for the first one that // has a saved location for this reg. bool - SearchForSavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation ®loc, uint32_t starting_frame_num, bool pc_or_return_address_reg); + SearchForSavedLocationForRegister (uint32_t lldb_regnum, lldb_private::UnwindLLDB::RegisterLocation ®loc, uint32_t starting_frame_num, bool pc_register); private: diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp index ff698be..9123804 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.cpp @@ -1005,16 +1005,6 @@ ProcessGDBRemote::DidAttach () DidLaunchOrAttach (); } -void -ProcessGDBRemote::DoDidExec () -{ - // The process exec'ed itself, figure out the dynamic loader, etc... - BuildDynamicRegisterInfo (true); - m_gdb_comm.ResetDiscoverableSettings(); - DidLaunchOrAttach (); -} - - Error ProcessGDBRemote::WillResume () @@ -1350,7 +1340,13 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) case 'T': case 'S': { - if (GetStopID() == 0) + // This is a bit of a hack, but is is required. If we did exec, we + // need to clear our thread lists and also know to rebuild our dynamic + // register info before we lookup and threads and populate the expedited + // register values so we need to know this right away so we can cleanup + // and update our registers. + const uint32_t stop_id = GetStopID(); + if (stop_id == 0) { // Our first stop, make sure we have a process ID, and also make // sure we know about our registers @@ -1507,6 +1503,7 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) else { bool handled = false; + bool did_exec = false; if (!reason.empty()) { if (reason.compare("trace") == 0) @@ -1552,9 +1549,15 @@ ProcessGDBRemote::SetThreadStopInfo (StringExtractor& stop_packet) thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithException(*thread_sp, description.c_str())); handled = true; } + else if (reason.compare("exec") == 0) + { + did_exec = true; + thread_sp->SetStopInfo (StopInfo::CreateStopReasonWithExec(*thread_sp)); + handled = true; + } } - if (signo) + if (signo && did_exec == false) { if (signo == SIGTRAP) { @@ -1881,6 +1884,26 @@ ProcessGDBRemote::DoDestroy () return error; } +void +ProcessGDBRemote::SetLastStopPacket (const StringExtractorGDBRemote &response) +{ + lldb_private::Mutex::Locker locker (m_last_stop_packet_mutex); + const bool did_exec = response.GetStringRef().find(";reason:exec;") != std::string::npos; + if (did_exec) + { + Log *log (ProcessGDBRemoteLog::GetLogIfAllCategoriesSet(GDBR_LOG_PROCESS)); + if (log) + log->Printf ("ProcessGDBRemote::SetLastStopPacket () - detected exec"); + + m_thread_list_real.Clear(); + m_thread_list.Clear(); + BuildDynamicRegisterInfo (true); + m_gdb_comm.ResetDiscoverableSettings(); + } + m_last_stop_packet = response; +} + + //------------------------------------------------------------------ // Process Queries //------------------------------------------------------------------ @@ -1931,7 +1954,7 @@ ProcessGDBRemote::DoReadMemory (addr_t addr, void *buf, size_t size, Error &erro } else { - error.SetErrorStringWithFormat("failed to sent packet: '%s'", packet); + error.SetErrorStringWithFormat("failed to send packet: '%s'", packet); } return 0; } @@ -1967,7 +1990,7 @@ ProcessGDBRemote::DoWriteMemory (addr_t addr, const void *buf, size_t size, Erro } else { - error.SetErrorStringWithFormat("failed to sent packet: '%s'", packet.GetString().c_str()); + error.SetErrorStringWithFormat("failed to send packet: '%s'", packet.GetString().c_str()); } return 0; } diff --git a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h index b42a61d..ab8fef3 100644 --- a/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h +++ b/lldb/source/Plugins/Process/gdb-remote/ProcessGDBRemote.h @@ -114,9 +114,6 @@ public: virtual void DidAttach (); - virtual void - DoDidExec (); - //------------------------------------------------------------------ // PluginInterface protocol //------------------------------------------------------------------ @@ -289,11 +286,7 @@ protected: BuildDynamicRegisterInfo (bool force); void - SetLastStopPacket (const StringExtractorGDBRemote &response) - { - lldb_private::Mutex::Locker locker (m_last_stop_packet_mutex); - m_last_stop_packet = response; - } + SetLastStopPacket (const StringExtractorGDBRemote &response); //------------------------------------------------------------------ /// Broadcaster event bits definitions. diff --git a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp index 8fc8729..8de74e1 100644 --- a/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp +++ b/lldb/source/Plugins/Process/mach-core/ProcessMachCore.cpp @@ -32,6 +32,7 @@ #include "ThreadMachCore.h" #include "StopInfoMachException.h" +// Needed for the plug-in names for the dynamic loaders. #include "Plugins/DynamicLoader/MacOSX-DYLD/DynamicLoaderMacOSXDYLD.h" #include "Plugins/DynamicLoader/Darwin-Kernel/DynamicLoaderDarwinKernel.h" @@ -212,6 +213,13 @@ ProcessMachCore::DoLoadCore () error.SetErrorString ("invalid core object file"); return error; } + + if (core_objfile->GetNumThreadContexts() == 0) + { + error.SetErrorString ("core file doesn't contain any LC_THREAD load commands, or the LC_THREAD architecture is not supported in this lldb"); + return error; + } + SectionList *section_list = core_objfile->GetSectionList(); if (section_list == NULL) { @@ -296,10 +304,26 @@ ProcessMachCore::DoLoadCore () if (m_dyld_addr == LLDB_INVALID_ADDRESS) { - addr_t kernel_load_address = DynamicLoaderDarwinKernel::SearchForDarwinKernel (this); - if (kernel_load_address != LLDB_INVALID_ADDRESS) + // We need to locate the main executable in the memory ranges + // we have in the core file. We already checked the first address + // in each memory zone above, so we just need to check each page + // except the first page in each range and stop once we have found + // our main executable + const size_t num_core_aranges = m_core_aranges.GetSize(); + for (size_t i=0; i<num_core_aranges && m_dyld_addr == LLDB_INVALID_ADDRESS; ++i) { - GetDynamicLoaderAddress (kernel_load_address); + const VMRangeToFileOffset::Entry *entry = m_core_aranges.GetEntryAtIndex(i); + lldb::addr_t section_vm_addr_start = entry->GetRangeBase(); + lldb::addr_t section_vm_addr_end = entry->GetRangeEnd(); + for (lldb::addr_t section_vm_addr = section_vm_addr_start + 0x1000; + section_vm_addr < section_vm_addr_end; + section_vm_addr += 0x1000) + { + if (GetDynamicLoaderAddress (section_vm_addr)) + { + break; + } + } } } return error; diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp index 9dd1dd4..392d2c93 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARF.cpp @@ -7175,174 +7175,171 @@ SymbolFileDWARF::ParseVariableDIE } } - if (location.IsValid()) + ValueType scope = eValueTypeInvalid; + + const DWARFDebugInfoEntry *sc_parent_die = GetParentSymbolContextDIE(die); + dw_tag_t parent_tag = sc_parent_die ? sc_parent_die->Tag() : 0; + SymbolContextScope * symbol_context_scope = NULL; + + // DWARF doesn't specify if a DW_TAG_variable is a local, global + // or static variable, so we have to do a little digging by + // looking at the location of a varaible to see if it contains + // a DW_OP_addr opcode _somewhere_ in the definition. I say + // somewhere because clang likes to combine small global variables + // into the same symbol and have locations like: + // DW_OP_addr(0x1000), DW_OP_constu(2), DW_OP_plus + // So if we don't have a DW_TAG_formal_parameter, we can look at + // the location to see if it contains a DW_OP_addr opcode, and + // then we can correctly classify our variables. + if (tag == DW_TAG_formal_parameter) + scope = eValueTypeVariableArgument; + else { - ValueType scope = eValueTypeInvalid; - - const DWARFDebugInfoEntry *sc_parent_die = GetParentSymbolContextDIE(die); - dw_tag_t parent_tag = sc_parent_die ? sc_parent_die->Tag() : 0; - SymbolContextScope * symbol_context_scope = NULL; - - // DWARF doesn't specify if a DW_TAG_variable is a local, global - // or static variable, so we have to do a little digging by - // looking at the location of a varaible to see if it contains - // a DW_OP_addr opcode _somewhere_ in the definition. I say - // somewhere because clang likes to combine small global variables - // into the same symbol and have locations like: - // DW_OP_addr(0x1000), DW_OP_constu(2), DW_OP_plus - // So if we don't have a DW_TAG_formal_parameter, we can look at - // the location to see if it contains a DW_OP_addr opcode, and - // then we can correctly classify our variables. - if (tag == DW_TAG_formal_parameter) - scope = eValueTypeVariableArgument; - else + bool op_error = false; + // Check if the location has a DW_OP_addr with any address value... + lldb::addr_t location_DW_OP_addr = LLDB_INVALID_ADDRESS; + if (!location_is_const_value_data) { - bool op_error = false; - // Check if the location has a DW_OP_addr with any address value... - lldb::addr_t location_DW_OP_addr = LLDB_INVALID_ADDRESS; - if (!location_is_const_value_data) + location_DW_OP_addr = location.GetLocation_DW_OP_addr (0, op_error); + if (op_error) { - location_DW_OP_addr = location.GetLocation_DW_OP_addr (0, op_error); - if (op_error) - { - StreamString strm; - location.DumpLocationForAddress (&strm, eDescriptionLevelFull, 0, 0, NULL); - GetObjectFile()->GetModule()->ReportError ("0x%8.8x: %s has an invalid location: %s", die->GetOffset(), DW_TAG_value_to_name(die->Tag()), strm.GetString().c_str()); - } + StreamString strm; + location.DumpLocationForAddress (&strm, eDescriptionLevelFull, 0, 0, NULL); + GetObjectFile()->GetModule()->ReportError ("0x%8.8x: %s has an invalid location: %s", die->GetOffset(), DW_TAG_value_to_name(die->Tag()), strm.GetString().c_str()); } + } - if (location_DW_OP_addr != LLDB_INVALID_ADDRESS) + if (location_DW_OP_addr != LLDB_INVALID_ADDRESS) + { + if (is_external) + scope = eValueTypeVariableGlobal; + else + scope = eValueTypeVariableStatic; + + + SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile (); + + if (debug_map_symfile) { - if (is_external) - scope = eValueTypeVariableGlobal; - else - scope = eValueTypeVariableStatic; - - - SymbolFileDWARFDebugMap *debug_map_symfile = GetDebugMapSymfile (); - - if (debug_map_symfile) + // When leaving the DWARF in the .o files on darwin, + // when we have a global variable that wasn't initialized, + // the .o file might not have allocated a virtual + // address for the global variable. In this case it will + // have created a symbol for the global variable + // that is undefined/data and external and the value will + // be the byte size of the variable. When we do the + // address map in SymbolFileDWARFDebugMap we rely on + // having an address, we need to do some magic here + // so we can get the correct address for our global + // variable. The address for all of these entries + // will be zero, and there will be an undefined symbol + // in this object file, and the executable will have + // a matching symbol with a good address. So here we + // dig up the correct address and replace it in the + // location for the variable, and set the variable's + // symbol context scope to be that of the main executable + // so the file address will resolve correctly. + bool linked_oso_file_addr = false; + if (is_external && location_DW_OP_addr == 0) { - // When leaving the DWARF in the .o files on darwin, - // when we have a global variable that wasn't initialized, - // the .o file might not have allocated a virtual - // address for the global variable. In this case it will - // have created a symbol for the global variable - // that is undefined/data and external and the value will - // be the byte size of the variable. When we do the - // address map in SymbolFileDWARFDebugMap we rely on - // having an address, we need to do some magic here - // so we can get the correct address for our global - // variable. The address for all of these entries - // will be zero, and there will be an undefined symbol - // in this object file, and the executable will have - // a matching symbol with a good address. So here we - // dig up the correct address and replace it in the - // location for the variable, and set the variable's - // symbol context scope to be that of the main executable - // so the file address will resolve correctly. - bool linked_oso_file_addr = false; - if (is_external && location_DW_OP_addr == 0) + + // we have a possible uninitialized extern global + ConstString const_name(mangled ? mangled : name); + ObjectFile *debug_map_objfile = debug_map_symfile->GetObjectFile(); + if (debug_map_objfile) { - - // we have a possible uninitialized extern global - ConstString const_name(mangled ? mangled : name); - ObjectFile *debug_map_objfile = debug_map_symfile->GetObjectFile(); - if (debug_map_objfile) + Symtab *debug_map_symtab = debug_map_objfile->GetSymtab(); + if (debug_map_symtab) { - Symtab *debug_map_symtab = debug_map_objfile->GetSymtab(); - if (debug_map_symtab) + Symbol *exe_symbol = debug_map_symtab->FindFirstSymbolWithNameAndType (const_name, + eSymbolTypeData, + Symtab::eDebugYes, + Symtab::eVisibilityExtern); + if (exe_symbol) { - Symbol *exe_symbol = debug_map_symtab->FindFirstSymbolWithNameAndType (const_name, - eSymbolTypeData, - Symtab::eDebugYes, - Symtab::eVisibilityExtern); - if (exe_symbol) + if (exe_symbol->ValueIsAddress()) { - if (exe_symbol->ValueIsAddress()) + const addr_t exe_file_addr = exe_symbol->GetAddress().GetFileAddress(); + if (exe_file_addr != LLDB_INVALID_ADDRESS) { - const addr_t exe_file_addr = exe_symbol->GetAddress().GetFileAddress(); - if (exe_file_addr != LLDB_INVALID_ADDRESS) + if (location.Update_DW_OP_addr (exe_file_addr)) { - if (location.Update_DW_OP_addr (exe_file_addr)) - { - linked_oso_file_addr = true; - symbol_context_scope = exe_symbol; - } + linked_oso_file_addr = true; + symbol_context_scope = exe_symbol; } } } } } } + } - if (!linked_oso_file_addr) + if (!linked_oso_file_addr) + { + // The DW_OP_addr is not zero, but it contains a .o file address which + // needs to be linked up correctly. + const lldb::addr_t exe_file_addr = debug_map_symfile->LinkOSOFileAddress(this, location_DW_OP_addr); + if (exe_file_addr != LLDB_INVALID_ADDRESS) { - // The DW_OP_addr is not zero, but it contains a .o file address which - // needs to be linked up correctly. - const lldb::addr_t exe_file_addr = debug_map_symfile->LinkOSOFileAddress(this, location_DW_OP_addr); - if (exe_file_addr != LLDB_INVALID_ADDRESS) - { - // Update the file address for this variable - location.Update_DW_OP_addr (exe_file_addr); - } - else - { - // Variable didn't make it into the final executable - return var_sp; - } + // Update the file address for this variable + location.Update_DW_OP_addr (exe_file_addr); + } + else + { + // Variable didn't make it into the final executable + return var_sp; } } } - else - { - scope = eValueTypeVariableLocal; - } } + else + { + scope = eValueTypeVariableLocal; + } + } - if (symbol_context_scope == NULL) + if (symbol_context_scope == NULL) + { + switch (parent_tag) { - switch (parent_tag) + case DW_TAG_subprogram: + case DW_TAG_inlined_subroutine: + case DW_TAG_lexical_block: + if (sc.function) { - case DW_TAG_subprogram: - case DW_TAG_inlined_subroutine: - case DW_TAG_lexical_block: - if (sc.function) - { - symbol_context_scope = sc.function->GetBlock(true).FindBlockByID(MakeUserID(sc_parent_die->GetOffset())); - if (symbol_context_scope == NULL) - symbol_context_scope = sc.function; - } - break; - - default: - symbol_context_scope = sc.comp_unit; - break; + symbol_context_scope = sc.function->GetBlock(true).FindBlockByID(MakeUserID(sc_parent_die->GetOffset())); + if (symbol_context_scope == NULL) + symbol_context_scope = sc.function; } + break; + + default: + symbol_context_scope = sc.comp_unit; + break; } + } - if (symbol_context_scope) - { - var_sp.reset (new Variable (MakeUserID(die->GetOffset()), - name, - mangled, - SymbolFileTypeSP (new SymbolFileType(*this, type_uid)), - scope, - symbol_context_scope, - &decl, - location, - is_external, - is_artificial)); - - var_sp->SetLocationIsConstantValueData (location_is_const_value_data); - } - else - { - // Not ready to parse this variable yet. It might be a global - // or static variable that is in a function scope and the function - // in the symbol context wasn't filled in yet - return var_sp; - } + if (symbol_context_scope) + { + var_sp.reset (new Variable (MakeUserID(die->GetOffset()), + name, + mangled, + SymbolFileTypeSP (new SymbolFileType(*this, type_uid)), + scope, + symbol_context_scope, + &decl, + location, + is_external, + is_artificial)); + + var_sp->SetLocationIsConstantValueData (location_is_const_value_data); + } + else + { + // Not ready to parse this variable yet. It might be a global + // or static variable that is in a function scope and the function + // in the symbol context wasn't filled in yet + return var_sp; } } // Cache var_sp even if NULL (the variable was just a specification or diff --git a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp index 3afe396..61f95f3 100644 --- a/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp +++ b/lldb/source/Plugins/SymbolFile/DWARF/SymbolFileDWARFDebugMap.cpp @@ -54,6 +54,8 @@ SymbolFileDWARFDebugMap::CompileUnitInfo::GetFileRangeMap(SymbolFileDWARFDebugMa return file_range_map; ObjectFile *oso_objfile = oso_module->GetObjectFile(); + if (!oso_objfile) + return file_range_map; Log *log (LogChannelDWARF::GetLogIfAll(DWARF_LOG_DEBUG_MAP)); if (log) diff --git a/lldb/source/Symbol/SymbolContext.cpp b/lldb/source/Symbol/SymbolContext.cpp index 7c83778..b2beb91 100644 --- a/lldb/source/Symbol/SymbolContext.cpp +++ b/lldb/source/Symbol/SymbolContext.cpp @@ -319,7 +319,6 @@ SymbolContext::GetResolvedMask () const return resolved_mask; } - void SymbolContext::Dump(Stream *s, Target *target) const { @@ -593,7 +592,7 @@ SymbolContext::GetFunctionMethodInfo (lldb::LanguageType &language, } ConstString -SymbolContext::GetFunctionName (Mangled::NamePreference preference) +SymbolContext::GetFunctionName (Mangled::NamePreference preference) const { if (function) { @@ -621,6 +620,33 @@ SymbolContext::GetFunctionName (Mangled::NamePreference preference) } } +LineEntry +SymbolContext::GetFunctionStartLineEntry () const +{ + LineEntry line_entry; + Address start_addr; + if (block) + { + Block *inlined_block = block->GetContainingInlinedBlock(); + if (inlined_block) + { + if (inlined_block->GetStartAddress (start_addr)) + { + if (start_addr.CalculateSymbolContextLineEntry (line_entry)) + return line_entry; + } + return LineEntry(); + } + } + + if (function) + { + if (function->GetAddressRange().GetBaseAddress().CalculateSymbolContextLineEntry(line_entry)) + return line_entry; + } + return LineEntry(); +} + //---------------------------------------------------------------------- // // SymbolContextSpecifier diff --git a/lldb/source/Symbol/Symtab.cpp b/lldb/source/Symbol/Symtab.cpp index c0f241e..b4c7554 100644 --- a/lldb/source/Symbol/Symtab.cpp +++ b/lldb/source/Symbol/Symtab.cpp @@ -320,7 +320,7 @@ Symtab::InitNameIndexes() entry.cstring[2] != 'Z')) // named local entities (if we eventually handle eSymbolTypeData, we will want this back) { CPPLanguageRuntime::MethodName cxx_method (mangled.GetDemangledName()); - entry.cstring = cxx_method.GetBasename ().GetCString(); + entry.cstring = ConstString(cxx_method.GetBasename()).GetCString(); if (entry.cstring && entry.cstring[0]) { // ConstString objects permanently store the string in the pool so calling diff --git a/lldb/source/Symbol/Variable.cpp b/lldb/source/Symbol/Variable.cpp index 9b7f4c5..bcbea2f 100644 --- a/lldb/source/Symbol/Variable.cpp +++ b/lldb/source/Symbol/Variable.cpp @@ -416,8 +416,8 @@ Variable::GetValuesForVariableExpressionPath (const char *variable_expr_path, ValueObjectSP variable_valobj_sp(ValueObjectVariable::Create (scope, var_sp)); if (variable_valobj_sp) { - variable_expr_path += variable_name.size(); - if (*variable_expr_path) + const char *variable_sub_expr_path = variable_expr_path + variable_name.size(); + if (*variable_sub_expr_path) { const char* first_unparsed = NULL; ValueObject::ExpressionPathScanEndReason reason_to_stop; @@ -425,7 +425,7 @@ Variable::GetValuesForVariableExpressionPath (const char *variable_expr_path, ValueObject::GetValueForExpressionPathOptions options; ValueObject::ExpressionPathAftermath final_task_on_target; - valobj_sp = variable_valobj_sp->GetValueForExpressionPath (variable_expr_path, + valobj_sp = variable_valobj_sp->GetValueForExpressionPath (variable_sub_expr_path, &first_unparsed, &reason_to_stop, &final_value_type, @@ -434,7 +434,7 @@ Variable::GetValuesForVariableExpressionPath (const char *variable_expr_path, if (!valobj_sp) { error.SetErrorStringWithFormat ("invalid expression path '%s' for variable '%s'", - variable_expr_path, + variable_sub_expr_path, var_sp->GetName().GetCString()); } } @@ -465,7 +465,7 @@ Variable::GetValuesForVariableExpressionPath (const char *variable_expr_path, } } } - error.SetErrorStringWithFormat ("unable to extracta variable name from '%s'", variable_expr_path); + error.SetErrorStringWithFormat ("unable to extract a variable name from '%s'", variable_expr_path); } break; } diff --git a/lldb/source/Target/CPPLanguageRuntime.cpp b/lldb/source/Target/CPPLanguageRuntime.cpp index b020e0a..f5b7f7f 100644 --- a/lldb/source/Target/CPPLanguageRuntime.cpp +++ b/lldb/source/Target/CPPLanguageRuntime.cpp @@ -9,6 +9,8 @@ #include "lldb/Target/CPPLanguageRuntime.h" +#include <string.h> + #include "lldb/Core/PluginManager.h" #include "lldb/Core/UniqueCStringMap.h" #include "lldb/Target/ExecutionContext.h" @@ -193,15 +195,7 @@ CPPLanguageRuntime::StripNamespacesFromVariableName (const char *name, const cha if (base_name_end == NULL) base_name_end = name + strlen (name); - const char *last_colon = NULL; - for (const char *ptr = base_name_end; ptr != name; ptr--) - { - if (*ptr == ':') - { - last_colon = ptr; - break; - } - } + const char *last_colon = strrchr (name, ':'); if (last_colon == NULL) { @@ -221,36 +215,6 @@ CPPLanguageRuntime::StripNamespacesFromVariableName (const char *name, const cha return true; } } -bool -CPPLanguageRuntime::IsPossibleCPPCall (const char *name, const char *&base_name_start, const char *&base_name_end) -{ - if (!name) - return false; - // For now, I really can't handle taking template names apart, so if you - // have < or > I'll say "could be CPP but leave the base_name empty which - // means I couldn't figure out what to use for that. - // FIXME: Do I need to do more sanity checking here? - - if (strchr(name, '>') != NULL || strchr (name, '>') != NULL) - return true; - - size_t name_len = strlen (name); - - if (name[name_len - 1] == ')') - { - // We've got arguments. - base_name_end = strchr (name, '('); - if (base_name_end == NULL) - return false; - - // FIXME: should check that this parenthesis isn't a template specialized - // on a function type or something gross like that... - } - else - base_name_end = name + strlen (name); - - return StripNamespacesFromVariableName (name, base_name_start, base_name_end); -} uint32_t CPPLanguageRuntime::FindEquivalentNames(ConstString type_name, std::vector<ConstString>& equivalents) @@ -272,7 +236,7 @@ void CPPLanguageRuntime::MethodName::Clear() { m_full.Clear(); - m_basename.Clear(); + m_basename = llvm::StringRef(); m_context = llvm::StringRef(); m_arguments = llvm::StringRef(); m_qualifiers = llvm::StringRef(); @@ -356,12 +320,12 @@ CPPLanguageRuntime::MethodName::Parse() context_end = full.rfind(':', basename_end); if (context_end == llvm::StringRef::npos) - m_basename.SetString(full.substr(0, basename_end)); + m_basename = full.substr(0, basename_end); else { m_context = full.substr(0, context_end - 1); const size_t basename_begin = context_end + 1; - m_basename.SetString(full.substr(basename_begin, basename_end - basename_begin)); + m_basename = full.substr(basename_begin, basename_end - basename_begin); } m_type = eTypeUnknownMethod; } @@ -388,7 +352,7 @@ CPPLanguageRuntime::MethodName::Parse() } } -const ConstString & +llvm::StringRef CPPLanguageRuntime::MethodName::GetBasename () { if (!m_parsed) diff --git a/lldb/source/Target/ExecutionContext.cpp b/lldb/source/Target/ExecutionContext.cpp index 6ab0355..8b5731e 100644 --- a/lldb/source/Target/ExecutionContext.cpp +++ b/lldb/source/Target/ExecutionContext.cpp @@ -806,9 +806,12 @@ ExecutionContextRef::GetThreadSP () const lldb::StackFrameSP ExecutionContextRef::GetFrameSP () const { - lldb::ThreadSP thread_sp (GetThreadSP()); - if (thread_sp) - return thread_sp->GetFrameWithStackID (m_stack_id); + if (m_stack_id.IsValid()) + { + lldb::ThreadSP thread_sp (GetThreadSP()); + if (thread_sp) + return thread_sp->GetFrameWithStackID (m_stack_id); + } return lldb::StackFrameSP(); } diff --git a/lldb/source/Target/Memory.cpp b/lldb/source/Target/Memory.cpp index ee475f2..3c8d483 100644 --- a/lldb/source/Target/Memory.cpp +++ b/lldb/source/Target/Memory.cpp @@ -40,10 +40,12 @@ MemoryCache::~MemoryCache() } void -MemoryCache::Clear() +MemoryCache::Clear(bool clear_invalid_ranges) { Mutex::Locker locker (m_mutex); m_cache.clear(); + if (clear_invalid_ranges) + m_invalid_ranges.Clear(); } void diff --git a/lldb/source/Target/Platform.cpp b/lldb/source/Target/Platform.cpp index 1293691d..e6d3bc7 100644 --- a/lldb/source/Target/Platform.cpp +++ b/lldb/source/Target/Platform.cpp @@ -118,7 +118,7 @@ Platform::FindPlugin (Process *process, const ConstString &plugin_name) { for (uint32_t idx = 0; (create_callback = PluginManager::GetPlatformCreateCallbackAtIndex(idx)) != NULL; ++idx) { - std::unique_ptr<Platform> instance_ap(create_callback(process, false)); + std::unique_ptr<Platform> instance_ap(create_callback(process, nullptr)); if (instance_ap.get()) return instance_ap.release(); } diff --git a/lldb/source/Target/Process.cpp b/lldb/source/Target/Process.cpp index 61b4ec6..1127446 100644 --- a/lldb/source/Target/Process.cpp +++ b/lldb/source/Target/Process.cpp @@ -996,6 +996,7 @@ Process::Process(Target &target, Listener &listener) : ProcessProperties (false), UserID (LLDB_INVALID_PROCESS_ID), Broadcaster (&(target.GetDebugger()), "lldb.process"), + m_reservation_cache (*this), m_target (target), m_public_state (eStateUnloaded), m_private_state (eStateUnloaded), @@ -1719,7 +1720,7 @@ Process::SetPrivateState (StateType new_state) else m_private_run_lock.WriteLock(); } - + if (state_changed) { m_private_state.SetValueNoLock (new_state); @@ -5582,10 +5583,69 @@ Process::DidExec () m_dynamic_checkers_ap.reset(); m_abi_sp.reset(); m_os_ap.reset(); - m_dyld_ap.reset(); + m_dyld_ap.reset(); m_image_tokens.clear(); m_allocated_memory_cache.Clear(); m_language_runtimes.clear(); + m_thread_list.DiscardThreadPlans(); + m_memory_cache.Clear(true); DoDidExec(); CompleteAttach (); } + +Process::ReservationCache::ReservationCache (Process &process) : m_process(process) +{ + m_mod_id = process.GetModID(); +} + +void +Process::ReservationCache::Reserve (lldb::addr_t addr, size_t size) +{ + CheckModID(); + m_reserved_cache[addr] = size; +} + +void +Process::ReservationCache::Unreserve (lldb::addr_t addr) +{ + CheckModID(); + ReservedMap::iterator iter = m_reserved_cache.find(addr); + + if (iter != m_reserved_cache.end()) + { + size_t size = iter->second; + m_reserved_cache.erase(iter); + m_free_cache[size].push_back(addr); + } +} + +lldb::addr_t +Process::ReservationCache::Find (size_t size) +{ + CheckModID(); + lldb::addr_t ret = LLDB_INVALID_ADDRESS; + FreeMap::iterator map_iter = m_free_cache.find(size); + if (map_iter != m_free_cache.end()) + { + if (!map_iter->second.empty()) + { + ret = map_iter->second.back(); + map_iter->second.pop_back(); + m_reserved_cache[ret] = size; + } + } + + return ret; +} + +void +Process::ReservationCache::CheckModID() +{ + if (m_mod_id != m_process.GetModID()) + { + // wipe all our caches, they're invalid + m_reserved_cache.clear(); + m_free_cache.clear(); + m_mod_id = m_process.GetModID(); + } +} diff --git a/lldb/source/Target/StackFrame.cpp b/lldb/source/Target/StackFrame.cpp index a1f581e..b0293be 100644 --- a/lldb/source/Target/StackFrame.cpp +++ b/lldb/source/Target/StackFrame.cpp @@ -1282,13 +1282,12 @@ StackFrame::DumpUsingSettingsFormat (Stream *strm) GetSymbolContext(eSymbolContextEverything); ExecutionContext exe_ctx (shared_from_this()); - const char *end = NULL; StreamString s; const char *frame_format = NULL; Target *target = exe_ctx.GetTargetPtr(); if (target) frame_format = target->GetDebugger().GetFrameFormat(); - if (frame_format && Debugger::FormatPrompt (frame_format, &m_sc, &exe_ctx, NULL, s, &end)) + if (frame_format && Debugger::FormatPrompt (frame_format, &m_sc, &exe_ctx, NULL, s)) { strm->Write(s.GetData(), s.GetSize()); } diff --git a/lldb/source/Target/StackFrameList.cpp b/lldb/source/Target/StackFrameList.cpp index ef798cb..69309df 100644 --- a/lldb/source/Target/StackFrameList.cpp +++ b/lldb/source/Target/StackFrameList.cpp @@ -595,19 +595,42 @@ StackFrameList::GetFrameWithConcreteFrameIndex (uint32_t unwind_idx) return frame_sp; } +static bool +CompareStackID (const StackFrameSP &stack_sp, const StackID &stack_id) +{ + return stack_sp->GetStackID() < stack_id; +} + StackFrameSP StackFrameList::GetFrameWithStackID (const StackID &stack_id) { - uint32_t frame_idx = 0; StackFrameSP frame_sp; - do + + if (stack_id.IsValid()) { - frame_sp = GetFrameAtIndex (frame_idx); - if (frame_sp && frame_sp->GetStackID() == stack_id) - break; - frame_idx++; + Mutex::Locker locker (m_mutex); + uint32_t frame_idx = 0; + // Do a binary search in case the stack frame is already in our cache + collection::const_iterator begin = m_frames.begin(); + collection::const_iterator end = m_frames.end(); + if (begin != end) + { + collection::const_iterator pos = std::lower_bound (begin, end, stack_id, CompareStackID); + if (pos != end && (*pos)->GetStackID() == stack_id) + return *pos; + + if (m_frames.back()->GetStackID() < stack_id) + frame_idx = m_frames.size(); + } + do + { + frame_sp = GetFrameAtIndex (frame_idx); + if (frame_sp && frame_sp->GetStackID() == stack_id) + break; + frame_idx++; + } + while (frame_sp); } - while (frame_sp); return frame_sp; } diff --git a/lldb/source/Target/Target.cpp b/lldb/source/Target/Target.cpp index f1a09a7..4e4290c 100644 --- a/lldb/source/Target/Target.cpp +++ b/lldb/source/Target/Target.cpp @@ -984,11 +984,16 @@ static void LoadScriptingResourceForModule (const ModuleSP &module_sp, Target *target) { Error error; - if (module_sp && !module_sp->LoadScriptingResourceInTarget(target, error)) + StreamString feedback_stream; + if (module_sp && !module_sp->LoadScriptingResourceInTarget(target, error, &feedback_stream)) { - target->GetDebugger().GetOutputStream().Printf("unable to load scripting data for module %s - error reported was %s\n", - module_sp->GetFileSpec().GetFileNameStrippingExtension().GetCString(), - error.AsCString()); + if (error.AsCString()) + target->GetDebugger().GetErrorStream().Printf("unable to load scripting data for module %s - error reported was %s\n", + module_sp->GetFileSpec().GetFileNameStrippingExtension().GetCString(), + error.AsCString()); + if (feedback_stream.GetSize()) + target->GetDebugger().GetOutputStream().Printf("%s\n", + feedback_stream.GetData()); } } @@ -1140,18 +1145,21 @@ Target::ModulesDidLoad (ModuleList &module_list) void Target::SymbolsDidLoad (ModuleList &module_list) { - if (module_list.GetSize() == 0) - return; - if (m_process_sp) + if (module_list.GetSize()) { - LanguageRuntime* runtime = m_process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); - if (runtime) + if (m_process_sp) { - ObjCLanguageRuntime *objc_runtime = (ObjCLanguageRuntime*)runtime; - objc_runtime->SymbolsDidLoad(module_list); + LanguageRuntime* runtime = m_process_sp->GetLanguageRuntime(lldb::eLanguageTypeObjC); + if (runtime) + { + ObjCLanguageRuntime *objc_runtime = (ObjCLanguageRuntime*)runtime; + objc_runtime->SymbolsDidLoad(module_list); + } } + + m_breakpoint_list.UpdateBreakpoints (module_list, true); + BroadcastEvent(eBroadcastBitSymbolsLoaded, NULL); } - BroadcastEvent(eBroadcastBitSymbolsLoaded, NULL); } void @@ -2265,6 +2273,15 @@ g_x86_dis_flavor_value_types[] = { 0, NULL, NULL } }; +static OptionEnumValueElement +g_load_script_from_sym_file_values[] = +{ + { eLoadScriptFromSymFileTrue, "true", "Load debug scripts inside symbol files"}, + { eLoadScriptFromSymFileFalse, "false", "Do not load debug scripts inside symbol files."}, + { eLoadScriptFromSymFileWarn, "warn", "Warn about debug scripts inside symbol files but do not load them."}, + { 0, NULL, NULL } +}; + static PropertyDefinition g_properties[] = { @@ -2281,6 +2298,7 @@ g_properties[] = { "exec-search-paths" , OptionValue::eTypeFileSpecList, false, 0 , NULL, NULL, "Executable search paths to use when locating executable files whose paths don't match the local file system." }, { "max-children-count" , OptionValue::eTypeSInt64 , false, 256 , NULL, NULL, "Maximum number of children to expand in any level of depth." }, { "max-string-summary-length" , OptionValue::eTypeSInt64 , false, 1024 , NULL, NULL, "Maximum number of characters to show when using %s in summary strings." }, + { "max-memory-read-size" , OptionValue::eTypeSInt64 , false, 1024 , NULL, NULL, "Maximum number of bytes that 'memory read' will fetch before --force must be specified." }, { "breakpoints-use-platform-avoid-list", OptionValue::eTypeBoolean , false, true , NULL, NULL, "Consult the platform module avoid list when setting non-module specific breakpoints." }, { "arg0" , OptionValue::eTypeString , false, 0 , NULL, NULL, "The first argument passed to the program in the argument array which can be different from the executable itself." }, { "run-args" , OptionValue::eTypeArgs , false, 0 , NULL, NULL, "A list containing all the arguments to be passed to the executable when it is run. Note that this does NOT include the argv[0] which is in target.arg0." }, @@ -2301,7 +2319,7 @@ g_properties[] = // FIXME: This is the wrong way to do per-architecture settings, but we don't have a general per architecture settings system in place yet. { "x86-disassembly-flavor" , OptionValue::eTypeEnum , false, eX86DisFlavorDefault, NULL, g_x86_dis_flavor_value_types, "The default disassembly flavor to use for x86 or x86-64 targets." }, { "use-fast-stepping" , OptionValue::eTypeBoolean , false, true, NULL, NULL, "Use a fast stepping algorithm based on running from branch to branch rather than instruction single-stepping." }, - { "load-script-from-symbol-file" , OptionValue::eTypeBoolean , false, false, NULL, NULL, "Allow LLDB to load scripting resources embedded in symbol files when available." }, + { "load-script-from-symbol-file" , OptionValue::eTypeEnum , false, eLoadScriptFromSymFileWarn, NULL, g_load_script_from_sym_file_values, "Allow LLDB to load scripting resources embedded in symbol files when available." }, { NULL , OptionValue::eTypeInvalid , false, 0 , NULL, NULL, NULL } }; enum @@ -2315,6 +2333,7 @@ enum ePropertyExecutableSearchPaths, ePropertyMaxChildrenCount, ePropertyMaxSummaryLength, + ePropertyMaxMemReadSize, ePropertyBreakpointUseAvoidList, ePropertyArg0, ePropertyRunArgs, @@ -2328,7 +2347,7 @@ enum ePropertyInlineStrategy, ePropertyDisassemblyFlavor, ePropertyUseFastStepping, - ePropertyLoadScriptFromSymbolFile + ePropertyLoadScriptFromSymbolFile, }; @@ -2373,6 +2392,13 @@ public: } return ProtectedGetPropertyAtIndex (idx); } + + lldb::TargetSP + GetTargetSP () + { + return m_target->shared_from_this(); + } + protected: void @@ -2603,6 +2629,13 @@ TargetProperties::GetMaximumSizeOfStringSummary() const return m_collection_sp->GetPropertyAtIndexAsSInt64 (NULL, idx, g_properties[idx].default_uint_value); } +uint32_t +TargetProperties::GetMaximumMemReadSize () const +{ + const uint32_t idx = ePropertyMaxMemReadSize; + return m_collection_sp->GetPropertyAtIndexAsSInt64 (NULL, idx, g_properties[idx].default_uint_value); +} + FileSpec TargetProperties::GetStandardInputPath () const { @@ -2674,18 +2707,11 @@ TargetProperties::GetUseFastStepping () const return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); } -bool +LoadScriptFromSymFile TargetProperties::GetLoadScriptFromSymbolFile () const { const uint32_t idx = ePropertyLoadScriptFromSymbolFile; - return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, g_properties[idx].default_uint_value != 0); -} - -void -TargetProperties::SetLoadScriptFromSymbolFile (bool b) -{ - const uint32_t idx = ePropertyLoadScriptFromSymbolFile; - m_collection_sp->SetPropertyAtIndexAsBoolean(NULL, idx, b); + return (LoadScriptFromSymFile)m_collection_sp->GetPropertyAtIndexAsEnumeration(NULL, idx, g_properties[idx].default_uint_value); } const TargetPropertiesSP & diff --git a/lldb/source/Target/Thread.cpp b/lldb/source/Target/Thread.cpp index 9abf514..0527cb1 100644 --- a/lldb/source/Target/Thread.cpp +++ b/lldb/source/Target/Thread.cpp @@ -442,6 +442,9 @@ Thread::SetStopInfo (const lldb::StopInfoSP &stop_info_sp) m_stop_info_stop_id = process_sp->GetStopID(); else m_stop_info_stop_id = UINT32_MAX; + Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_THREAD)); + if (log) + log->Printf("%p: tid = 0x%" PRIx64 ": stop info = %s (stop_id = %u)\n", this, GetID(), stop_info_sp ? stop_info_sp->GetDescription() : "<NULL>", m_stop_info_stop_id); } void @@ -1744,13 +1747,11 @@ Thread::DumpUsingSettingsFormat (Stream &strm, uint32_t frame_idx) const char *thread_format = exe_ctx.GetTargetRef().GetDebugger().GetThreadFormat(); assert (thread_format); - const char *end = NULL; Debugger::FormatPrompt (thread_format, frame_sp ? &frame_sc : NULL, &exe_ctx, NULL, - strm, - &end); + strm); } void diff --git a/lldb/source/Target/ThreadPlanBase.cpp b/lldb/source/Target/ThreadPlanBase.cpp index 998cc08..240f23a 100644 --- a/lldb/source/Target/ThreadPlanBase.cpp +++ b/lldb/source/Target/ThreadPlanBase.cpp @@ -102,10 +102,10 @@ ThreadPlanBase::ShouldStop (Event *event_ptr) Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); - StopInfoSP stop_info_sp = GetPrivateStopReason(); + StopInfoSP stop_info_sp = GetPrivateStopInfo (); if (stop_info_sp) { - StopReason reason = stop_info_sp->GetStopReason(); + StopReason reason = stop_info_sp->GetStopReason (); switch (reason) { case eStopReasonInvalid: diff --git a/lldb/source/Target/ThreadPlanCallFunction.cpp b/lldb/source/Target/ThreadPlanCallFunction.cpp index 0a77d0d..c9baaaf 100644 --- a/lldb/source/Target/ThreadPlanCallFunction.cpp +++ b/lldb/source/Target/ThreadPlanCallFunction.cpp @@ -298,7 +298,7 @@ ThreadPlanCallFunction::DoTakedown (bool success) log->Printf ("ThreadPlanCallFunction(%p): DoTakedown called for thread 0x%4.4" PRIx64 ", m_valid: %d complete: %d.\n", this, m_thread.GetID(), m_valid, IsPlanComplete()); m_takedown_done = true; m_stop_address = m_thread.GetStackFrameAtIndex(0)->GetRegisterContext()->GetPC(); - m_real_stop_info_sp = GetPrivateStopReason(); + m_real_stop_info_sp = GetPrivateStopInfo (); m_thread.RestoreRegisterStateFromCheckpoint(m_stored_thread_state); SetPlanComplete(success); ClearBreakpoints(); @@ -365,7 +365,7 @@ bool ThreadPlanCallFunction::DoPlanExplainsStop (Event *event_ptr) { Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP|LIBLLDB_LOG_PROCESS)); - m_real_stop_info_sp = GetPrivateStopReason(); + m_real_stop_info_sp = GetPrivateStopInfo (); // If our subplan knows why we stopped, even if it's done (which would forward the question to us) // we answer yes. @@ -584,14 +584,22 @@ ThreadPlanCallFunction::ClearBreakpoints () bool ThreadPlanCallFunction::BreakpointsExplainStop() { - StopInfoSP stop_info_sp = GetPrivateStopReason(); + StopInfoSP stop_info_sp = GetPrivateStopInfo (); if ((m_cxx_language_runtime && m_cxx_language_runtime->ExceptionBreakpointsExplainStop(stop_info_sp)) ||(m_objc_language_runtime && m_objc_language_runtime->ExceptionBreakpointsExplainStop(stop_info_sp))) { + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_STEP)); + if (log) + log->Printf ("ThreadPlanCallFunction::BreakpointsExplainStop - Hit an exception breakpoint, setting plan complete."); + SetPlanComplete(false); + + // If the user has set the ObjC language breakpoint, it would normally get priority over our internal + // catcher breakpoint, but in this case we can't let that happen, so force the ShouldStop here. + stop_info_sp->OverrideShouldStop (true); return true; } diff --git a/lldb/source/Target/ThreadPlanCallUserExpression.cpp b/lldb/source/Target/ThreadPlanCallUserExpression.cpp index 6e47808..70de1cb 100644 --- a/lldb/source/Target/ThreadPlanCallUserExpression.cpp +++ b/lldb/source/Target/ThreadPlanCallUserExpression.cpp @@ -67,12 +67,16 @@ StopInfoSP ThreadPlanCallUserExpression::GetRealStopInfo() { StopInfoSP stop_info_sp = ThreadPlanCallFunction::GetRealStopInfo(); - lldb::addr_t addr = GetStopAddress(); - DynamicCheckerFunctions *checkers = m_thread.GetProcess()->GetDynamicCheckers(); - StreamString s; - if (checkers && checkers->DoCheckersExplainStop(addr, s)) - stop_info_sp->SetDescription(s.GetData()); + if (stop_info_sp) + { + lldb::addr_t addr = GetStopAddress(); + DynamicCheckerFunctions *checkers = m_thread.GetProcess()->GetDynamicCheckers(); + StreamString s; + + if (checkers && checkers->DoCheckersExplainStop(addr, s)) + stop_info_sp->SetDescription(s.GetData()); + } return stop_info_sp; } diff --git a/lldb/source/Target/ThreadPlanStepInRange.cpp b/lldb/source/Target/ThreadPlanStepInRange.cpp index c5bb144..b7ebfea 100644 --- a/lldb/source/Target/ThreadPlanStepInRange.cpp +++ b/lldb/source/Target/ThreadPlanStepInRange.cpp @@ -403,7 +403,7 @@ ThreadPlanStepInRange::DoPlanExplainsStop (Event *event_ptr) } else { - StopInfoSP stop_info_sp = GetPrivateStopReason(); + StopInfoSP stop_info_sp = GetPrivateStopInfo (); if (stop_info_sp) { StopReason reason = stop_info_sp->GetStopReason(); diff --git a/lldb/source/Target/ThreadPlanStepInstruction.cpp b/lldb/source/Target/ThreadPlanStepInstruction.cpp index fa45b2b..f7a962e 100644 --- a/lldb/source/Target/ThreadPlanStepInstruction.cpp +++ b/lldb/source/Target/ThreadPlanStepInstruction.cpp @@ -83,7 +83,7 @@ ThreadPlanStepInstruction::ValidatePlan (Stream *error) bool ThreadPlanStepInstruction::DoPlanExplainsStop (Event *event_ptr) { - StopInfoSP stop_info_sp = GetPrivateStopReason(); + StopInfoSP stop_info_sp = GetPrivateStopInfo (); if (stop_info_sp) { StopReason reason = stop_info_sp->GetStopReason(); diff --git a/lldb/source/Target/ThreadPlanStepOut.cpp b/lldb/source/Target/ThreadPlanStepOut.cpp index 86f99b2..71ccf80 100644 --- a/lldb/source/Target/ThreadPlanStepOut.cpp +++ b/lldb/source/Target/ThreadPlanStepOut.cpp @@ -200,7 +200,7 @@ ThreadPlanStepOut::DoPlanExplainsStop (Event *event_ptr) // We don't explain signals or breakpoints (breakpoints that handle stepping in or // out will be handled by a child plan. - StopInfoSP stop_info_sp = GetPrivateStopReason(); + StopInfoSP stop_info_sp = GetPrivateStopInfo (); if (stop_info_sp) { StopReason reason = stop_info_sp->GetStopReason(); diff --git a/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp b/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp index ff4fee7..dc011e5 100644 --- a/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp +++ b/lldb/source/Target/ThreadPlanStepOverBreakpoint.cpp @@ -61,7 +61,7 @@ ThreadPlanStepOverBreakpoint::ValidatePlan (Stream *error) bool ThreadPlanStepOverBreakpoint::DoPlanExplainsStop (Event *event_ptr) { - StopInfoSP stop_info_sp = GetPrivateStopReason(); + StopInfoSP stop_info_sp = GetPrivateStopInfo (); if (stop_info_sp) { StopReason reason = stop_info_sp->GetStopReason(); diff --git a/lldb/source/Target/ThreadPlanStepOverRange.cpp b/lldb/source/Target/ThreadPlanStepOverRange.cpp index 3381292..679ef46 100644 --- a/lldb/source/Target/ThreadPlanStepOverRange.cpp +++ b/lldb/source/Target/ThreadPlanStepOverRange.cpp @@ -300,7 +300,7 @@ ThreadPlanStepOverRange::DoPlanExplainsStop (Event *event_ptr) // unexplained breakpoint/crash. Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_STEP)); - StopInfoSP stop_info_sp = GetPrivateStopReason(); + StopInfoSP stop_info_sp = GetPrivateStopInfo (); bool return_value; if (stop_info_sp) diff --git a/lldb/source/Target/ThreadPlanStepUntil.cpp b/lldb/source/Target/ThreadPlanStepUntil.cpp index ca3d30d..7f35d04 100644 --- a/lldb/source/Target/ThreadPlanStepUntil.cpp +++ b/lldb/source/Target/ThreadPlanStepUntil.cpp @@ -176,7 +176,7 @@ ThreadPlanStepUntil::AnalyzeStop() if (m_ran_analyze) return; - StopInfoSP stop_info_sp = GetPrivateStopReason(); + StopInfoSP stop_info_sp = GetPrivateStopInfo (); m_should_stop = true; m_explains_stop = false; @@ -320,7 +320,7 @@ ThreadPlanStepUntil::ShouldStop (Event *event_ptr) // do so here. Otherwise, as long as this thread has stopped for a reason, // we will stop. - StopInfoSP stop_info_sp = GetPrivateStopReason(); + StopInfoSP stop_info_sp = GetPrivateStopInfo (); if (!stop_info_sp || stop_info_sp->GetStopReason() == eStopReasonNone) return false; diff --git a/lldb/source/Utility/StringExtractor.h b/lldb/source/Utility/StringExtractor.h index df4a97a..0ded310 100644 --- a/lldb/source/Utility/StringExtractor.h +++ b/lldb/source/Utility/StringExtractor.h @@ -73,6 +73,12 @@ public: return m_packet; } + const std::string & + GetStringRef () const + { + return m_packet; + } + bool Empty() { diff --git a/lldb/source/lldb-log.cpp b/lldb/source/lldb-log.cpp index d6a568d..c53fa56 100644 --- a/lldb/source/lldb-log.cpp +++ b/lldb/source/lldb-log.cpp @@ -142,6 +142,7 @@ lldb_private::DisableLog (const char **categories, Stream *feedback_strm) else if (0 == ::strncasecmp(arg, "symbol", 6)) flag_bits &= ~LIBLLDB_LOG_SYMBOLS; else if (0 == ::strncasecmp(arg, "module", 6)) flag_bits &= ~LIBLLDB_LOG_MODULES; else if (0 == ::strncasecmp(arg, "mmap", 4)) flag_bits &= ~LIBLLDB_LOG_MMAP; + else if (0 == ::strcasecmp(arg, "os")) flag_bits &= ~LIBLLDB_LOG_OS; else { feedback_strm->Printf ("error: unrecognized log category '%s'\n", arg); @@ -211,6 +212,7 @@ lldb_private::EnableLog (StreamSP &log_stream_sp, uint32_t log_options, const ch else if (0 == ::strncasecmp(arg, "symbol", 6)) flag_bits |= LIBLLDB_LOG_SYMBOLS; else if (0 == ::strncasecmp(arg, "module", 6)) flag_bits |= LIBLLDB_LOG_MODULES; else if (0 == ::strncasecmp(arg, "mmap", 4)) flag_bits |= LIBLLDB_LOG_MMAP; + else if (0 == ::strcasecmp(arg, "os")) flag_bits |= LIBLLDB_LOG_OS; else { feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); diff --git a/lldb/test/dotest.py b/lldb/test/dotest.py index 4044551..9bf1cba 100755 --- a/lldb/test/dotest.py +++ b/lldb/test/dotest.py @@ -1071,7 +1071,7 @@ def lldbLoggings(): raise Exception('log enable failed (check GDB_REMOTE_LOG env variable.') def getMyCommandLine(): - ps = subprocess.Popen(['ps', '-o', "command=CMD", str(os.getpid())], stdout=subprocess.PIPE).communicate()[0] + ps = subprocess.Popen([which('ps'), '-o', "command=CMD", str(os.getpid())], stdout=subprocess.PIPE).communicate()[0] lines = ps.split('\n') cmd_line = lines[1] return cmd_line diff --git a/lldb/test/expression_command/call-throws/TestCallThatThrows.py b/lldb/test/expression_command/call-throws/TestCallThatThrows.py index 596c3ef..8a849b3 100644 --- a/lldb/test/expression_command/call-throws/TestCallThatThrows.py +++ b/lldb/test/expression_command/call-throws/TestCallThatThrows.py @@ -1,5 +1,5 @@ """ -Test calling a function that hits a signal set to auto-restart, make sure the call completes. +Test calling a function that throws an ObjC exception, make sure that it doesn't propagate the exception. """ import unittest2 @@ -7,7 +7,7 @@ import lldb import lldbutil from lldbtest import * -class ExprCommandWithTimeoutsTestCase(TestBase): +class ExprCommandWithThrowTestCase(TestBase): mydir = os.path.join("expression_command", "call-throws") @@ -22,14 +22,14 @@ class ExprCommandWithTimeoutsTestCase(TestBase): @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") @dsym_test def test_with_dsym(self): - """Test calling std::String member function.""" + """Test calling a function that throws and ObjC exception.""" self.buildDsym() self.call_function() @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin due to ObjC test case") @dwarf_test def test_with_dwarf(self): - """Test calling std::String member function.""" + """Test calling a function that throws and ObjC exception.""" self.buildDwarf() self.call_function() @@ -40,7 +40,7 @@ class ExprCommandWithTimeoutsTestCase(TestBase): def call_function(self): - """Test calling function with timeout.""" + """Test calling function that throws.""" exe_name = "a.out" exe = os.path.join(os.getcwd(), exe_name) @@ -86,6 +86,18 @@ class ExprCommandWithTimeoutsTestCase(TestBase): self.assertTrue (value.IsValid() and value.GetError().Success() == False) self.check_after_call() + # Now set the ObjC language breakpoint and make sure that doesn't interfere with the call: + exception_bkpt = target.BreakpointCreateForException (lldb.eLanguageTypeObjC, False, True) + self.assertTrue(exception_bkpt.GetNumLocations() > 0) + + options.SetIgnoreBreakpoints(True) + options.SetUnwindOnError(True) + + value = frame.EvaluateExpression ("[my_class callMeIThrow]", options) + + self.assertTrue (value.IsValid() and value.GetError().Success() == False) + self.check_after_call() + # Now set this unwind on error to false, and make sure that we stop where the exception was thrown options.SetUnwindOnError(False) value = frame.EvaluateExpression ("[my_class callMeIThrow]", options) diff --git a/lldb/test/expression_command/persistent_variables/TestPersistentVariables.py b/lldb/test/expression_command/persistent_variables/TestPersistentVariables.py index 47d1631..2a10108 100644 --- a/lldb/test/expression_command/persistent_variables/TestPersistentVariables.py +++ b/lldb/test/expression_command/persistent_variables/TestPersistentVariables.py @@ -17,30 +17,38 @@ class PersistentVariablesTestCase(TestBase): self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) - self.runCmd("breakpoint set --name main") + self.runCmd("breakpoint set --source-pattern-regexp break") self.runCmd("run", RUN_SUCCEEDED) - self.expect("expression int $i = 5; $i + 1", - startstr = "(int) $0 = 6") - # (int) $0 = 6 + self.runCmd("expression int $i = i") + + self.expect("expression $i == i", + startstr = "(bool) $0 = true") + + self.expect("expression $i + 1", + startstr = "(int) $1 = 6") self.expect("expression $i + 3", - startstr = "(int) $1 = 8") - # (int) $1 = 8 + startstr = "(int) $2 = 8") - self.expect("expression $1 + $0", - startstr = "(int) $2 = 14") - # (int) $2 = 14 + self.expect("expression $2 + $1", + startstr = "(int) $3 = 14") + + self.expect("expression $3", + startstr = "(int) $3 = 14") self.expect("expression $2", - startstr = "(int) $2 = 14") - # (int) $2 = 14 + startstr = "(int) $2 = 8") + + self.expect("expression (int)-2", + startstr = "(int) $4 = -2") - self.expect("expression $1", - startstr = "(int) $1 = 8") - # (int) $1 = 8 + self.expect("expression $4 > (int)31", + startstr = "(bool) $5 = false") + self.expect("expression (long)$4", + startstr = "(long) $6 = -2") if __name__ == '__main__': import atexit diff --git a/lldb/test/expression_command/persistent_variables/main.c b/lldb/test/expression_command/persistent_variables/main.c index 343eac7..fd41471 100644 --- a/lldb/test/expression_command/persistent_variables/main.c +++ b/lldb/test/expression_command/persistent_variables/main.c @@ -9,5 +9,6 @@ int main (int argc, char const *argv[]) { - return 0; + int i = 5; + return 0; // Set breakpoint here } diff --git a/lldb/test/functionalities/abbreviation/TestAbbreviations.py b/lldb/test/functionalities/abbreviation/TestAbbreviations.py index e9247cc..d828d1b 100644 --- a/lldb/test/functionalities/abbreviation/TestAbbreviations.py +++ b/lldb/test/functionalities/abbreviation/TestAbbreviations.py @@ -14,7 +14,7 @@ class AbbreviationsTestCase(TestBase): def test_nonrunning_command_abbreviations (self): self.expect("ap script", - startstr = "The following commands may relate to 'script':", + startstr = "The following built-in commands may relate to 'script':", substrs = ['breakpoint command add', 'breakpoint command list', 'breakpoint list', diff --git a/lldb/test/functionalities/command_script/import/TestImport.py b/lldb/test/functionalities/command_script/import/TestImport.py index 2356fce..2f0121b 100644 --- a/lldb/test/functionalities/command_script/import/TestImport.py +++ b/lldb/test/functionalities/command_script/import/TestImport.py @@ -45,17 +45,15 @@ class ImportTestCase(TestBase): error=True, startstr='error: module importing failed') self.expect("command script import ./nosuchfolder/", error=True, startstr='error: module importing failed') - self.expect("command script import ./foo/foo.py", - error=True, startstr='error: module importing failed') + self.expect("command script import ./foo/foo.py", error=False) self.runCmd("command script import --allow-reload ./thepackage") self.expect("TPcommandA",substrs=["hello world A"]) self.expect("TPcommandB",substrs=["hello world B"]) self.runCmd("script import dummymodule") - self.expect("command script import ./dummymodule.py", - error=True, startstr='error: module importing failed') - self.runCmd("command script import --allow-reload ./dummymodule.py") + self.expect("command script import ./dummymodule.py", error=False) + self.expect("command script import --allow-reload ./dummymodule.py", error=False) self.runCmd("command script add -f foo.foo_function foocmd") self.runCmd("command script add -f foobar.foo_function foobarcmd") diff --git a/lldb/test/functionalities/data-formatter/data-formatter-advanced/TestDataFormatterAdv.py b/lldb/test/functionalities/data-formatter/data-formatter-advanced/TestDataFormatterAdv.py index 15596b3..9feffbe 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-advanced/TestDataFormatterAdv.py +++ b/lldb/test/functionalities/data-formatter/data-formatter-advanced/TestDataFormatterAdv.py @@ -77,7 +77,8 @@ class AdvDataFormatterTestCase(TestBase): self.expect("frame variable int_array", substrs = ['1,2,3,4,5']) - self.runCmd("type summary add --summary-string \"${var[].integer}\" -x \"i_am_cool \\[[0-9]\\]") + # this will fail if we don't do [] as regex correctly + self.runCmd('type summary add --summary-string "${var[].integer}" "i_am_cool[]') self.expect("frame variable cool_array", substrs = ['1,1,1,1,6']) @@ -122,7 +123,12 @@ class AdvDataFormatterTestCase(TestBase): self.runCmd("type summary clear") - self.runCmd("type summary add --summary-string \"${var[0-1]}\" -x \"int \[[0-9]\]\"") + self.runCmd('type summary add --summary-string \"${var[0-1]}\" -x \"int \[[0-9]\]\"') + + self.expect("frame variable int_array", + substrs = ['1,2']) + + self.runCmd('type summary add --summary-string \"${var[0-1]}\" "int []"') self.expect("frame variable int_array", substrs = ['1,2']) diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/TestDataFormatterLibccMap.py b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/TestDataFormatterLibccMap.py index ff5a026..05a96e3 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/TestDataFormatterLibccMap.py +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/map/TestDataFormatterLibccMap.py @@ -58,17 +58,15 @@ class LibcxxMapDataFormatterTestCase(TestBase): self.expect('image list',substrs=['libc++.1.dylib','libc++abi.dylib']) self.runCmd("frame variable ii --show-types") - - self.runCmd("type summary add -x \"std::__1::map<\" --summary-string \"map has ${svar%#} items\" -e") - + self.expect('frame variable ii', - substrs = ['map has 0 items', + substrs = ['size=0', '{}']) self.runCmd("continue"); self.expect('frame variable ii', - substrs = ['map has 2 items', + substrs = ['size=2', '[0] = {', 'first = 0', 'second = 0', @@ -79,7 +77,7 @@ class LibcxxMapDataFormatterTestCase(TestBase): self.runCmd("continue"); self.expect('frame variable ii', - substrs = ['map has 4 items', + substrs = ['size=4', '[2] = {', 'first = 2', 'second = 0', @@ -90,7 +88,7 @@ class LibcxxMapDataFormatterTestCase(TestBase): self.runCmd("continue"); self.expect("frame variable ii", - substrs = ['map has 8 items', + substrs = ['size=8', '[5] = {', 'first = 5', 'second = 0', @@ -99,7 +97,7 @@ class LibcxxMapDataFormatterTestCase(TestBase): 'second = 1']) self.expect("p ii", - substrs = ['map has 8 items', + substrs = ['size=8', '[5] = {', 'first = 5', 'second = 0', @@ -129,19 +127,19 @@ class LibcxxMapDataFormatterTestCase(TestBase): self.runCmd("continue"); self.expect('frame variable ii', - substrs = ['map has 0 items', + substrs = ['size=0', '{}']) self.runCmd("frame variable si --show-types") self.expect('frame variable si', - substrs = ['map has 0 items', + substrs = ['size=0', '{}']) self.runCmd("continue"); self.expect('frame variable si', - substrs = ['map has 1 items', + substrs = ['size=1', '[0] = ', 'first = \"zero\"', 'second = 0']) @@ -149,7 +147,7 @@ class LibcxxMapDataFormatterTestCase(TestBase): self.runCmd("continue"); self.expect("frame variable si", - substrs = ['map has 4 items', + substrs = ['size=4', '[0] = ', 'first = \"zero\"', 'second = 0', @@ -164,7 +162,7 @@ class LibcxxMapDataFormatterTestCase(TestBase): 'second = 3']) self.expect("p si", - substrs = ['map has 4 items', + substrs = ['size=4', '[0] = ', 'first = \"zero\"', 'second = 0', @@ -197,20 +195,20 @@ class LibcxxMapDataFormatterTestCase(TestBase): self.runCmd("continue"); self.expect('frame variable si', - substrs = ['map has 0 items', + substrs = ['size=0', '{}']) self.runCmd("continue"); self.runCmd("frame variable is --show-types") self.expect('frame variable is', - substrs = ['map has 0 items', + substrs = ['size=0', '{}']) self.runCmd("continue"); self.expect("frame variable is", - substrs = ['map has 4 items', + substrs = ['size=4', '[0] = ', 'second = \"goofy\"', 'first = 85', @@ -225,7 +223,7 @@ class LibcxxMapDataFormatterTestCase(TestBase): 'first = 3']) self.expect("p is", - substrs = ['map has 4 items', + substrs = ['size=4', '[0] = ', 'second = \"goofy\"', 'first = 85', @@ -258,20 +256,20 @@ class LibcxxMapDataFormatterTestCase(TestBase): self.runCmd("continue"); self.expect('frame variable is', - substrs = ['map has 0 items', + substrs = ['size=0', '{}']) self.runCmd("continue"); self.runCmd("frame variable ss --show-types") self.expect('frame variable ss', - substrs = ['map has 0 items', + substrs = ['size=0', '{}']) self.runCmd("continue"); self.expect("frame variable ss", - substrs = ['map has 3 items', + substrs = ['size=3', '[0] = ', 'second = \"hello\"', 'first = \"ciao\"', @@ -283,7 +281,7 @@ class LibcxxMapDataFormatterTestCase(TestBase): 'first = \"gatto\"']) self.expect("p ss", - substrs = ['map has 3 items', + substrs = ['size=3', '[0] = ', 'second = \"hello\"', 'first = \"ciao\"', @@ -312,7 +310,7 @@ class LibcxxMapDataFormatterTestCase(TestBase): self.runCmd("continue"); self.expect('frame variable ss', - substrs = ['map has 0 items', + substrs = ['size=0', '{}']) if __name__ == '__main__': diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/multimap/Makefile b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/multimap/Makefile new file mode 100644 index 0000000..e681e09 --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/multimap/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules + +CXXFLAGS += -stdlib=libc++ -O0 -std=c++11 +LDFLAGS += -stdlib=libc++
\ No newline at end of file diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/multimap/TestDataFormatterLibccMultiMap.py b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/multimap/TestDataFormatterLibccMultiMap.py new file mode 100644 index 0000000..bb2bec1 --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/multimap/TestDataFormatterLibccMultiMap.py @@ -0,0 +1,320 @@ +""" +Test lldb data formatter subsystem. +""" + +import os, time +import unittest2 +import lldb +from lldbtest import * +import lldbutil + +class LibcxxMultiMapDataFormatterTestCase(TestBase): + + mydir = os.path.join("functionalities", "data-formatter", "data-formatter-stl", "libcxx", "multimap") + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @dsym_test + def test_with_dsym_and_run_command(self): + """Test data formatter commands.""" + self.buildDsym() + self.data_formatter_commands() + + @skipIfLinux # No standard locations for libc++ on Linux, so skip for now + @dwarf_test + def test_with_dwarf_and_run_command(self): + """Test data formatter commands.""" + self.buildDwarf() + self.data_formatter_commands() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + def data_formatter_commands(self): + """Test that that file and class static variables display correctly.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_source_regexp (self, "Set break point at this line.") + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.expect('image list',substrs=['libc++.1.dylib','libc++abi.dylib']) + + self.runCmd("frame variable ii --show-types") + + self.expect('frame variable ii', + substrs = ['size=0', + '{}']) + + self.runCmd("continue"); + + self.expect('frame variable ii', + substrs = ['size=2', + '[0] = {', + 'first = 0', + 'second = 0', + '[1] = {', + 'first = 1', + 'second = 1']) + + self.runCmd("continue"); + + self.expect('frame variable ii', + substrs = ['size=4', + '[2] = {', + 'first = 2', + 'second = 0', + '[3] = {', + 'first = 3', + 'second = 1']) + + self.runCmd("continue"); + + self.expect("frame variable ii", + substrs = ['size=8', + '[5] = {', + 'first = 5', + 'second = 0', + '[7] = {', + 'first = 7', + 'second = 1']) + + self.expect("p ii", + substrs = ['size=8', + '[5] = {', + 'first = 5', + 'second = 0', + '[7] = {', + 'first = 7', + 'second = 1']) + + # check access-by-index + self.expect("frame variable ii[0]", + substrs = ['first = 0', + 'second = 0']); + self.expect("frame variable ii[3]", + substrs = ['first =', + 'second =']); + + # check that MightHaveChildren() gets it right + self.assertTrue(self.frame().FindVariable("ii").MightHaveChildren(), "ii.MightHaveChildren() says False for non empty!") + + # check that the expression parser does not make use of + # synthetic children instead of running code + # TOT clang has a fix for this, which makes the expression command here succeed + # since this would make the test fail or succeed depending on clang version in use + # this is safer commented for the time being + #self.expect("expression ii[8]", matching=False, error=True, + # substrs = ['1234567']) + + self.runCmd("continue"); + + self.expect('frame variable ii', + substrs = ['size=0', + '{}']) + + self.runCmd("frame variable si --show-types") + + self.expect('frame variable si', + substrs = ['size=0', + '{}']) + + self.runCmd("continue"); + + self.expect('frame variable si', + substrs = ['size=1', + '[0] = ', + 'first = \"zero\"', + 'second = 0']) + + self.runCmd("continue"); + + self.expect("frame variable si", + substrs = ['size=4', + '[0] = ', + 'first = \"zero\"', + 'second = 0', + '[1] = ', + 'first = \"one\"', + 'second = 1', + '[2] = ', + 'first = \"two\"', + 'second = 2', + '[3] = ', + 'first = \"three\"', + 'second = 3']) + + self.expect("p si", + substrs = ['size=4', + '[0] = ', + 'first = \"zero\"', + 'second = 0', + '[1] = ', + 'first = \"one\"', + 'second = 1', + '[2] = ', + 'first = \"two\"', + 'second = 2', + '[3] = ', + 'first = \"three\"', + 'second = 3']) + + # check that MightHaveChildren() gets it right + self.assertTrue(self.frame().FindVariable("si").MightHaveChildren(), "si.MightHaveChildren() says False for non empty!") + + # check access-by-index + self.expect("frame variable si[0]", + substrs = ['first = ', 'one', + 'second = 1']); + + # check that the expression parser does not make use of + # synthetic children instead of running code + # TOT clang has a fix for this, which makes the expression command here succeed + # since this would make the test fail or succeed depending on clang version in use + # this is safer commented for the time being + #self.expect("expression si[0]", matching=False, error=True, + # substrs = ['first = ', 'zero']) + + self.runCmd("continue"); + + self.expect('frame variable si', + substrs = ['size=0', + '{}']) + + self.runCmd("continue"); + self.runCmd("frame variable is --show-types") + + self.expect('frame variable is', + substrs = ['size=0', + '{}']) + + self.runCmd("continue"); + + self.expect("frame variable is", + substrs = ['size=4', + '[0] = ', + 'second = \"goofy\"', + 'first = 85', + '[1] = ', + 'second = \"is\"', + 'first = 1', + '[2] = ', + 'second = \"smart\"', + 'first = 2', + '[3] = ', + 'second = \"!!!\"', + 'first = 3']) + + self.expect("p is", + substrs = ['size=4', + '[0] = ', + 'second = \"goofy\"', + 'first = 85', + '[1] = ', + 'second = \"is\"', + 'first = 1', + '[2] = ', + 'second = \"smart\"', + 'first = 2', + '[3] = ', + 'second = \"!!!\"', + 'first = 3']) + + # check that MightHaveChildren() gets it right + self.assertTrue(self.frame().FindVariable("is").MightHaveChildren(), "is.MightHaveChildren() says False for non empty!") + + # check access-by-index + self.expect("frame variable is[0]", + substrs = ['first = ', + 'second =']); + + # check that the expression parser does not make use of + # synthetic children instead of running code + # TOT clang has a fix for this, which makes the expression command here succeed + # since this would make the test fail or succeed depending on clang version in use + # this is safer commented for the time being + #self.expect("expression is[0]", matching=False, error=True, + # substrs = ['first = ', 'goofy']) + + self.runCmd("continue"); + + self.expect('frame variable is', + substrs = ['size=0', + '{}']) + + self.runCmd("continue"); + self.runCmd("frame variable ss --show-types") + + self.expect('frame variable ss', + substrs = ['size=0', + '{}']) + + self.runCmd("continue"); + + self.expect("frame variable ss", + substrs = ['size=3', + '[0] = ', + 'second = \"hello\"', + 'first = \"ciao\"', + '[1] = ', + 'second = \"house\"', + 'first = \"casa\"', + '[2] = ', + 'second = \"cat\"', + 'first = \"gatto\"']) + + self.expect("p ss", + substrs = ['size=3', + '[0] = ', + 'second = \"hello\"', + 'first = \"ciao\"', + '[1] = ', + 'second = \"house\"', + 'first = \"casa\"', + '[2] = ', + 'second = \"cat\"', + 'first = \"gatto\"']) + + # check that MightHaveChildren() gets it right + self.assertTrue(self.frame().FindVariable("ss").MightHaveChildren(), "ss.MightHaveChildren() says False for non empty!") + + # check access-by-index + self.expect("frame variable ss[2]", + substrs = ['gatto', 'cat']); + + # check that the expression parser does not make use of + # synthetic children instead of running code + # TOT clang has a fix for this, which makes the expression command here succeed + # since this would make the test fail or succeed depending on clang version in use + # this is safer commented for the time being + #self.expect("expression ss[3]", matching=False, error=True, + # substrs = ['gatto']) + + self.runCmd("continue"); + + self.expect('frame variable ss', + substrs = ['size=0', + '{}']) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/multimap/main.cpp b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/multimap/main.cpp new file mode 100644 index 0000000..f1a6e2a --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/multimap/main.cpp @@ -0,0 +1,81 @@ +#include <string> +#ifdef _LIBCPP_INLINE_VISIBILITY +#undef _LIBCPP_INLINE_VISIBILITY +#endif +#define _LIBCPP_INLINE_VISIBILITY +#include <map> + +#define intint_map std::multimap<int, int> +#define strint_map std::multimap<std::string, int> +#define intstr_map std::multimap<int, std::string> +#define strstr_map std::multimap<std::string, std::string> + +int g_the_foo = 0; + +int thefoo_rw(int arg = 1) +{ + if (arg < 0) + arg = 0; + if (!arg) + arg = 1; + g_the_foo += arg; + return g_the_foo; +} + +int main() +{ + intint_map ii; + + ii.emplace(0,0); // Set break point at this line. + ii.emplace(1,1); + thefoo_rw(1); // Set break point at this line. + ii.emplace(2,0); + ii.emplace(3,1); + thefoo_rw(1); // Set break point at this line. + ii.emplace(4,0); + ii.emplace(5,1); + ii.emplace(6,0); + ii.emplace(7,1); + thefoo_rw(1); // Set break point at this line. + ii.emplace(85,1234567); + + ii.clear(); + + strint_map si; + thefoo_rw(1); // Set break point at this line. + + si.emplace("zero",0); + thefoo_rw(1); // Set break point at this line. + si.emplace("one",1); + si.emplace("two",2); + si.emplace("three",3); + thefoo_rw(1); // Set break point at this line. + si.emplace("four",4); + + si.clear(); + thefoo_rw(1); // Set break point at this line. + + intstr_map is; + thefoo_rw(1); // Set break point at this line. + is.emplace(85,"goofy"); + is.emplace(1,"is"); + is.emplace(2,"smart"); + is.emplace(3,"!!!"); + thefoo_rw(1); // Set break point at this line. + + is.clear(); + thefoo_rw(1); // Set break point at this line. + + strstr_map ss; + thefoo_rw(1); // Set break point at this line. + + ss.emplace("ciao","hello"); + ss.emplace("casa","house"); + ss.emplace("gatto","cat"); + thefoo_rw(1); // Set break point at this line. + ss.emplace("a Mac..","..is always a Mac!"); + + ss.clear(); + thefoo_rw(1); // Set break point at this line. + return 0; +}
\ No newline at end of file diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/Makefile b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/Makefile new file mode 100644 index 0000000..f2f2b3f --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules + +CXXFLAGS += -stdlib=libc++ -O0 +LDFLAGS += -stdlib=libc++
\ No newline at end of file diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/TestDataFormatterLibcxxMultiSet.py b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/TestDataFormatterLibcxxMultiSet.py new file mode 100644 index 0000000..7ee0a14 --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/TestDataFormatterLibcxxMultiSet.py @@ -0,0 +1,85 @@ +""" +Test lldb data formatter subsystem. +""" + +import os, time +import unittest2 +import lldb +from lldbtest import * +import lldbutil + +class LibcxxMultiSetDataFormatterTestCase(TestBase): + + mydir = os.path.join("functionalities", "data-formatter", "data-formatter-stl", "libcxx", "multiset") + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @dsym_test + def test_with_dsym_and_run_command(self): + """Test data formatter commands.""" + self.buildDsym() + self.data_formatter_commands() + + @skipIfLinux # No standard locations for libc++ on Linux, so skip for now + @dwarf_test + def test_with_dwarf_and_run_command(self): + """Test data formatter commands.""" + self.buildDwarf() + self.data_formatter_commands() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + def data_formatter_commands(self): + """Test that that file and class static variables display correctly.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_source_regexp (self, "Set break point at this line.") + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.expect('image list',substrs=['libc++.1.dylib','libc++abi.dylib']) + + self.expect("frame variable ii",substrs = ["size=0","{}"]) + self.runCmd("continue") + self.expect("frame variable ii",substrs = ["size=6","[0] = 0","[1] = 1", "[2] = 2", "[3] = 3", "[4] = 4", "[5] = 5"]) + self.runCmd("continue") + self.expect("frame variable ii",substrs = ["size=7","[2] = 2", "[3] = 3", "[6] = 6"]) + self.expect("p ii",substrs = ["size=7","[2] = 2", "[3] = 3", "[6] = 6"]) + self.expect("frame variable ii[2]",substrs = [" = 2"]) + self.runCmd("continue") + self.expect("frame variable ii",substrs = ["size=0","{}"]) + self.runCmd("continue") + self.expect("frame variable ii",substrs = ["size=0","{}"]) + self.expect("frame variable ss",substrs = ["size=0","{}"]) + self.runCmd("continue") + self.expect("frame variable ss",substrs = ["size=2",'[0] = "a"','[1] = "a very long string is right here"']) + self.runCmd("continue") + self.expect("frame variable ss",substrs = ["size=4",'[2] = "b"','[3] = "c"','[0] = "a"','[1] = "a very long string is right here"']) + self.expect("p ss",substrs = ["size=4",'[2] = "b"','[3] = "c"','[0] = "a"','[1] = "a very long string is right here"']) + self.expect("frame variable ss[2]",substrs = [' = "b"']) + self.runCmd("continue") + self.expect("frame variable ss",substrs = ["size=3",'[0] = "a"','[1] = "a very long string is right here"','[2] = "c"']) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/main.cpp b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/main.cpp new file mode 100644 index 0000000..1e1dd3b --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/multiset/main.cpp @@ -0,0 +1,57 @@ +#include <string> +#ifdef _LIBCPP_INLINE_VISIBILITY +#undef _LIBCPP_INLINE_VISIBILITY +#endif +#define _LIBCPP_INLINE_VISIBILITY +#include <set> + +typedef std::multiset<int> intset; +typedef std::multiset<std::string> stringset; + +int g_the_foo = 0; + +int thefoo_rw(int arg = 1) +{ + if (arg < 0) + arg = 0; + if (!arg) + arg = 1; + g_the_foo += arg; + return g_the_foo; +} + +int main() +{ + intset ii; + thefoo_rw(1); // Set break point at this line. + + ii.insert(0); + ii.insert(1); + ii.insert(2); + ii.insert(3); + ii.insert(4); + ii.insert(5); + thefoo_rw(1); // Set break point at this line. + + ii.insert(6); + thefoo_rw(1); // Set break point at this line. + + ii.clear(); + thefoo_rw(1); // Set break point at this line. + + stringset ss; + thefoo_rw(1); // Set break point at this line. + + ss.insert("a"); + ss.insert("a very long string is right here"); + thefoo_rw(1); // Set break point at this line. + + ss.insert("b"); + ss.insert("c"); + thefoo_rw(1); // Set break point at this line. + + ss.erase("b"); + thefoo_rw(1); // Set break point at this line. + + return 0; +} diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/Makefile b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/Makefile new file mode 100644 index 0000000..f2f2b3f --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules + +CXXFLAGS += -stdlib=libc++ -O0 +LDFLAGS += -stdlib=libc++
\ No newline at end of file diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/TestDataFormatterLibcxxSet.py b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/TestDataFormatterLibcxxSet.py new file mode 100644 index 0000000..e1d9171 --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/TestDataFormatterLibcxxSet.py @@ -0,0 +1,85 @@ +""" +Test lldb data formatter subsystem. +""" + +import os, time +import unittest2 +import lldb +from lldbtest import * +import lldbutil + +class LibcxxSetDataFormatterTestCase(TestBase): + + mydir = os.path.join("functionalities", "data-formatter", "data-formatter-stl", "libcxx", "set") + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @dsym_test + def test_with_dsym_and_run_command(self): + """Test data formatter commands.""" + self.buildDsym() + self.data_formatter_commands() + + @skipIfLinux # No standard locations for libc++ on Linux, so skip for now + @dwarf_test + def test_with_dwarf_and_run_command(self): + """Test data formatter commands.""" + self.buildDwarf() + self.data_formatter_commands() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + + def data_formatter_commands(self): + """Test that that file and class static variables display correctly.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_source_regexp (self, "Set break point at this line.") + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + self.runCmd('type format clear', check=False) + self.runCmd('type summary clear', check=False) + self.runCmd('type filter clear', check=False) + self.runCmd('type synth clear', check=False) + self.runCmd("settings set target.max-children-count 256", check=False) + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + self.expect('image list',substrs=['libc++.1.dylib','libc++abi.dylib']) + + self.expect("frame variable ii",substrs = ["size=0","{}"]) + self.runCmd("continue") + self.expect("frame variable ii",substrs = ["size=6","[0] = 0","[1] = 1", "[2] = 2", "[3] = 3", "[4] = 4", "[5] = 5"]) + self.runCmd("continue") + self.expect("frame variable ii",substrs = ["size=7","[2] = 2", "[3] = 3", "[6] = 6"]) + self.expect("frame variable ii[2]",substrs = [" = 2"]) + self.expect("p ii",substrs = ["size=7","[2] = 2", "[3] = 3", "[6] = 6"]) + self.runCmd("continue") + self.expect("frame variable ii",substrs = ["size=0","{}"]) + self.runCmd("continue") + self.expect("frame variable ii",substrs = ["size=0","{}"]) + self.expect("frame variable ss",substrs = ["size=0","{}"]) + self.runCmd("continue") + self.expect("frame variable ss",substrs = ["size=2",'[0] = "a"','[1] = "a very long string is right here"']) + self.runCmd("continue") + self.expect("frame variable ss",substrs = ["size=4",'[2] = "b"','[3] = "c"','[0] = "a"','[1] = "a very long string is right here"']) + self.expect("p ss",substrs = ["size=4",'[2] = "b"','[3] = "c"','[0] = "a"','[1] = "a very long string is right here"']) + self.expect("frame variable ss[2]",substrs = [' = "b"']) + self.runCmd("continue") + self.expect("frame variable ss",substrs = ["size=3",'[0] = "a"','[1] = "a very long string is right here"','[2] = "c"']) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/main.cpp b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/main.cpp new file mode 100644 index 0000000..cc3033e --- /dev/null +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libcxx/set/main.cpp @@ -0,0 +1,57 @@ +#include <string> +#ifdef _LIBCPP_INLINE_VISIBILITY +#undef _LIBCPP_INLINE_VISIBILITY +#endif +#define _LIBCPP_INLINE_VISIBILITY +#include <set> + +typedef std::set<int> intset; +typedef std::set<std::string> stringset; + +int g_the_foo = 0; + +int thefoo_rw(int arg = 1) +{ + if (arg < 0) + arg = 0; + if (!arg) + arg = 1; + g_the_foo += arg; + return g_the_foo; +} + +int main() +{ + intset ii; + thefoo_rw(1); // Set break point at this line. + + ii.insert(0); + ii.insert(1); + ii.insert(2); + ii.insert(3); + ii.insert(4); + ii.insert(5); + thefoo_rw(1); // Set break point at this line. + + ii.insert(6); + thefoo_rw(1); // Set break point at this line. + + ii.clear(); + thefoo_rw(1); // Set break point at this line. + + stringset ss; + thefoo_rw(1); // Set break point at this line. + + ss.insert("a"); + ss.insert("a very long string is right here"); + thefoo_rw(1); // Set break point at this line. + + ss.insert("b"); + ss.insert("c"); + thefoo_rw(1); // Set break point at this line. + + ss.erase("b"); + thefoo_rw(1); // Set break point at this line. + + return 0; +} diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/iterator/Makefile b/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/iterator/Makefile index 3b26f43..dd3264d 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/iterator/Makefile +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/iterator/Makefile @@ -2,7 +2,7 @@ LEVEL = ../../../../../make CXX_SOURCES := main.cpp -include $(LEVEL)/Makefile.rules +CFLAGS_EXTRAS := -O0 +USE_LIBSTDCPP := 1 -CXXFLAGS += -stdlib=libstdc++ -O0 -LDFLAGS += -stdlib=libstdc++ +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/Makefile b/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/Makefile index b736f92..dd3264d 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/Makefile +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/Makefile @@ -2,7 +2,7 @@ LEVEL = ../../../../../make CXX_SOURCES := main.cpp -include $(LEVEL)/Makefile.rules +CFLAGS_EXTRAS := -O0 +USE_LIBSTDCPP := 1 -CXXFLAGS += -stdlib=libstdc++ -O0 -LDFLAGS += -stdlib=libstdc++
\ No newline at end of file +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/TestDataFormatterStdList.py b/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/TestDataFormatterStdList.py index c5dc357..b76ff02 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/TestDataFormatterStdList.py +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/TestDataFormatterStdList.py @@ -20,17 +20,33 @@ class StdListDataFormatterTestCase(TestBase): self.data_formatter_commands() @dwarf_test - @expectedFailureGcc # llvm.org/pr15301 LLDB prints incorrect sizes of STL containers + @expectedFailureLinux('llvm.org/pr15301', ['gcc', 'icc']) # LLDB prints incorrect sizes of STL containers def test_with_dwarf_and_run_command(self): """Test data formatter commands.""" self.buildDwarf() self.data_formatter_commands() + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @dsym_test + def test_with_dsym_and_run_command(self): + """Test data formatter commands.""" + self.buildDsym() + self.data_formatter_commands_after_steps() + + @dwarf_test + @expectedFailureLinux # llvm.org/pr15301 Multiple LLDB steps do not all complete synchronously. + def test_with_dwarf_and_run_command_after_steps(self): + """Test data formatter commands with multiple steps.""" + self.buildDwarf() + self.data_formatter_commands_after_steps() + def setUp(self): # Call super's setUp(). TestBase.setUp(self) - # Find the line number to break at. + # Find the line numbers to break at for the different tests. self.line = line_number('main.cpp', '// Set break point at this line.') + self.optional_line = line_number('main.cpp', '// Optional break point at this line.') + self.final_line = line_number('main.cpp', '// Set final break point at this line.') def data_formatter_commands(self): """Test that that file and class static variables display correctly.""" @@ -159,7 +175,14 @@ class StdListDataFormatterTestCase(TestBase): substrs = ['list has 0 items', '{}']) - self.runCmd("n");self.runCmd("n");self.runCmd("n");self.runCmd("n"); + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.final_line, num_expected_locations=-1) + + self.runCmd("c", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) self.expect("frame variable text_list", substrs = ['list has 4 items', @@ -188,6 +211,46 @@ class StdListDataFormatterTestCase(TestBase): # check that MightHaveChildren() gets it right self.assertTrue(self.frame().FindVariable("text_list").MightHaveChildren(), "text_list.MightHaveChildren() says False for non empty!") + def data_formatter_commands_after_steps(self): + """Test that that file and class static variables display correctly after multiple step instructions.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.optional_line, num_expected_locations=-1) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + self.expect("frame variable text_list", + substrs = ['{}']) + + # Verify that steps in rapid succession are processed prior to inspecting text_list. + self.runCmd("n");self.runCmd("n");self.runCmd("n");self.runCmd("n"); + + self.expect("frame variable text_list", + substrs = ['list has 4 items', + '[0]', 'goofy', + '[1]', 'is', + '[2]', 'smart', + '[3]', '!!!']) + + self.expect("p text_list", + substrs = ['list has 4 items', + '\"goofy\"', + '\"is\"', + '\"smart\"', + '\"!!!\"']) + + # check access-by-index + self.expect("frame variable text_list[0]", + substrs = ['goofy']); + self.expect("frame variable text_list[3]", + substrs = ['!!!']); + + if __name__ == '__main__': import atexit lldb.SBDebugger.Initialize() diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/main.cpp b/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/main.cpp index 87ea65c..191acdc 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/main.cpp +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/list/main.cpp @@ -23,11 +23,12 @@ int main() numbers_list.push_back(4); string_list text_list; - text_list.push_back(std::string("goofy")); + text_list.push_back(std::string("goofy")); // Optional break point at this line. text_list.push_back(std::string("is")); text_list.push_back(std::string("smart")); text_list.push_back(std::string("!!!")); - return 0; -}
\ No newline at end of file + return 0; // Set final break point at this line. +} + diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/map/Makefile b/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/map/Makefile index 3b26f43..17868d8 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/map/Makefile +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/map/Makefile @@ -2,7 +2,6 @@ LEVEL = ../../../../../make CXX_SOURCES := main.cpp -include $(LEVEL)/Makefile.rules +USE_LIBSTDCPP := 1 -CXXFLAGS += -stdlib=libstdc++ -O0 -LDFLAGS += -stdlib=libstdc++ +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/Makefile b/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/Makefile index 3b26f43..dd3264d 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/Makefile +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/string/Makefile @@ -2,7 +2,7 @@ LEVEL = ../../../../../make CXX_SOURCES := main.cpp -include $(LEVEL)/Makefile.rules +CFLAGS_EXTRAS := -O0 +USE_LIBSTDCPP := 1 -CXXFLAGS += -stdlib=libstdc++ -O0 -LDFLAGS += -stdlib=libstdc++ +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vbool/Makefile b/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vbool/Makefile index e6e6101..dd3264d 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vbool/Makefile +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vbool/Makefile @@ -2,10 +2,7 @@ LEVEL = ../../../../../make CXX_SOURCES := main.cpp -include $(LEVEL)/Makefile.rules +CFLAGS_EXTRAS := -O0 +USE_LIBSTDCPP := 1 -CXXFLAGS += -O0 -ifeq (,$(findstring gcc,$(CC))) -CXXFLAGS += -stdlib=libstdc++ -LDFLAGS += -stdlib=libstdc++ -endif +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vector/Makefile b/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vector/Makefile index b736f92..63f2aaf 100644 --- a/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vector/Makefile +++ b/lldb/test/functionalities/data-formatter/data-formatter-stl/libstdcpp/vector/Makefile @@ -2,7 +2,7 @@ LEVEL = ../../../../../make CXX_SOURCES := main.cpp -include $(LEVEL)/Makefile.rules +CXXFLAGS := -O0 +USE_LIBSTDCPP := 1 -CXXFLAGS += -stdlib=libstdc++ -O0 -LDFLAGS += -stdlib=libstdc++
\ No newline at end of file +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/data-formatter/format-propagation/Makefile b/lldb/test/functionalities/data-formatter/format-propagation/Makefile new file mode 100644 index 0000000..314f1cb --- /dev/null +++ b/lldb/test/functionalities/data-formatter/format-propagation/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/data-formatter/format-propagation/TestFormatPropagation.py b/lldb/test/functionalities/data-formatter/format-propagation/TestFormatPropagation.py new file mode 100644 index 0000000..3c24512 --- /dev/null +++ b/lldb/test/functionalities/data-formatter/format-propagation/TestFormatPropagation.py @@ -0,0 +1,91 @@ +""" +Check if changing Format on an SBValue correctly propagates that new format to children as it should +""" + +import os, time +import unittest2 +import lldb +from lldbtest import * +import lldbutil + +class FormatPropagationTestCase(TestBase): + + mydir = os.path.join("functionalities", "data-formatter", "format-propagation") + + # rdar://problem/14035604 + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @dsym_test + def test_with_dsym_and_run_command(self): + """Check if changing Format on an SBValue correctly propagates that new format to children as it should""" + self.buildDsym() + self.propagate_test_commands() + + # rdar://problem/14035604 + @dwarf_test + def test_with_dwarf_and_run_command(self): + """Check if changing Format on an SBValue correctly propagates that new format to children as it should""" + self.buildDwarf() + self.propagate_test_commands() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number to break at. + self.line = line_number('main.cpp', '// Set break point at this line.') + + def propagate_test_commands(self): + """Check for an issue where capping does not work because the Target pointer appears to be changing behind our backs.""" + self.runCmd("file a.out", CURRENT_EXECUTABLE_SET) + + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True) + + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # This is the function to remove the custom formats in order to have a + # clean slate for the next test case. + def cleanup(): + pass + + # Execute the cleanup function during test case tear down. + self.addTearDownHook(cleanup) + + # extract the parent and the children + frame = self.frame() + parent = self.frame().FindVariable("f") + self.assertTrue(parent != None and parent.IsValid(),"could not find f") + X = parent.GetChildMemberWithName("X") + self.assertTrue(X != None and X.IsValid(),"could not find X") + Y = parent.GetChildMemberWithName("Y") + self.assertTrue(Y != None and Y.IsValid(),"could not find Y") + # check their values now + self.assertTrue(X.GetValue() == "1", "X has an invalid value") + self.assertTrue(Y.GetValue() == "2", "Y has an invalid value") + # set the format on the parent + parent.SetFormat(lldb.eFormatHex) + self.assertTrue(X.GetValue() == "0x00000001", "X has not changed format") + self.assertTrue(Y.GetValue() == "0x00000002", "Y has not changed format") + # Step and check if the values make sense still + self.runCmd("next") + self.assertTrue(X.GetValue() == "0x00000004", "X has not become 4") + self.assertTrue(Y.GetValue() == "0x00000002", "Y has not stuck as hex") + # Check that children can still make their own choices + Y.SetFormat(lldb.eFormatDecimal) + self.assertTrue(X.GetValue() == "0x00000004", "X is still hex") + self.assertTrue(Y.GetValue() == "2", "Y has not been reset") + # Make a few more changes + parent.SetFormat(lldb.eFormatDefault) + X.SetFormat(lldb.eFormatHex) + Y.SetFormat(lldb.eFormatDefault) + self.assertTrue(X.GetValue() == "0x00000004", "X is not hex as it asked") + self.assertTrue(Y.GetValue() == "2", "Y is not defaulted") + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/functionalities/data-formatter/format-propagation/main.cpp b/lldb/test/functionalities/data-formatter/format-propagation/main.cpp new file mode 100644 index 0000000..5822fbc --- /dev/null +++ b/lldb/test/functionalities/data-formatter/format-propagation/main.cpp @@ -0,0 +1,13 @@ +struct foo +{ + int X; + int Y; + foo(int a, int b) : X(a), Y(b) {} +}; + +int main() +{ + foo f(1,2); + f.X = 4; // Set break point at this line. + return 0; +} diff --git a/lldb/test/functionalities/data-formatter/rdar-9973865/Makefile b/lldb/test/functionalities/data-formatter/rdar-9973865/Makefile index 314f1cb..32d3f4fa 100644 --- a/lldb/test/functionalities/data-formatter/rdar-9973865/Makefile +++ b/lldb/test/functionalities/data-formatter/rdar-9973865/Makefile @@ -2,4 +2,10 @@ LEVEL = ../../../make CXX_SOURCES := main.cpp +# Workaround for llvm.org/pr16214: clang doesn't emit structure definition DWARF +# information without the flag below. +ifneq (,$(findstring clang,$(CC))) + CFLAGS_EXTRAS := -fno-limit-debug-info +endif + include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/data-formatter/rdar-9973865/Test-rdar-9973865.py b/lldb/test/functionalities/data-formatter/rdar-9973865/Test-rdar-9973865.py index 6c0467c..0aa619b 100644 --- a/lldb/test/functionalities/data-formatter/rdar-9973865/Test-rdar-9973865.py +++ b/lldb/test/functionalities/data-formatter/rdar-9973865/Test-rdar-9973865.py @@ -23,6 +23,7 @@ class Radar9973865DataFormatterTestCase(TestBase): @dwarf_test def test_with_dwarf_and_run_command(self): """Test data formatter commands.""" + self.buildDwarf() self.data_formatter_commands() diff --git a/lldb/test/functionalities/exec/Makefile b/lldb/test/functionalities/exec/Makefile new file mode 100644 index 0000000..8a7102e --- /dev/null +++ b/lldb/test/functionalities/exec/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +CXX_SOURCES := main.cpp + +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/exec/TestExec.py b/lldb/test/functionalities/exec/TestExec.py new file mode 100644 index 0000000..e9a0dca --- /dev/null +++ b/lldb/test/functionalities/exec/TestExec.py @@ -0,0 +1,105 @@ +""" +Test some lldb command abbreviations. +""" +import commands +import lldb +import os +import time +import unittest2 +from lldbtest import * +import lldbutil + +def execute_command (command): + #print '%% %s' % (command) + (exit_status, output) = commands.getstatusoutput (command) + #if output: + # print output + #print 'status = %u' % (exit_status) + return exit_status + +class ExecTestCase(TestBase): + + mydir = os.path.join("functionalities", "exec") + + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @dsym_test + def test_with_dsym (self): + if self.getArchitecture() == 'x86_64': + source = os.path.join (os.getcwd(), "main.cpp") + o_file = os.path.join (os.getcwd(), "main.o") + execute_command ("'%s' -g -O0 -arch i386 -arch x86_64 '%s' -c -o '%s'" % (os.environ["CC"], source, o_file)) + execute_command ("'%s' -g -O0 -arch i386 -arch x86_64 '%s'" % (os.environ["CC"], o_file)) + else: + self.buildDsym() + self.do_test () + + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @dwarf_test + def test_with_dwarf (self): + if self.getArchitecture() == 'x86_64': + source = os.path.join (os.getcwd(), "main.cpp") + o_file = os.path.join (os.getcwd(), "main.o") + dsym_path = os.path.join (os.getcwd(), "a.out.dSYM") + execute_command ("'%s' -g -O0 -arch i386 -arch x86_64 '%s' -c -o '%s'" % (os.environ["CC"], source, o_file)) + execute_command ("'%s' -g -O0 -arch i386 -arch x86_64 '%s'" % (os.environ["CC"], o_file)) + execute_command ("rm -rf '%s'" % (dsym_path)) + else: + self.buildDwarf() + self.do_test () + + def do_test (self): + exe = os.path.join (os.getcwd(), "a.out") + + # Create the target + target = self.dbg.CreateTarget(exe) + + # Create any breakpoints we need + breakpoint = target.BreakpointCreateBySourceRegex ('Set breakpoint 1 here', lldb.SBFileSpec ("main.cpp", False)) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Launch the process + process = target.LaunchSimple(None, None, os.getcwd()) + self.assertTrue(process, PROCESS_IS_VALID) + + for i in range(6): + # The stop reason of the thread should be breakpoint. + self.assertTrue(process.GetState() == lldb.eStateStopped, + STOPPED_DUE_TO_BREAKPOINT) + + thread = process.GetThreadAtIndex (0) + + self.assertTrue (thread.IsValid(), + "Process stopped at 'main' should have a valid thread"); + + stop_reason = thread.GetStopReason() + + self.assertTrue (stop_reason == lldb.eStopReasonBreakpoint, + "Thread in process stopped in 'main' should have a stop reason of eStopReasonBreakpoint"); + + # Run and we should stop due to exec + process.Continue() + + self.assertTrue(process.GetState() == lldb.eStateStopped, + "Process should be stopped at __dyld_start") + + thread = process.GetThreadAtIndex (0) + + self.assertTrue (thread.IsValid(), + "Process stopped at exec should have a valid thread"); + + stop_reason = thread.GetStopReason() + + self.assertTrue (stop_reason == lldb.eStopReasonExec, + "Thread in process stopped on exec should have a stop reason of eStopReasonExec"); + + # Run and we should stop at breakpoint in main after exec + process.Continue() + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() + diff --git a/lldb/test/functionalities/exec/main.cpp b/lldb/test/functionalities/exec/main.cpp new file mode 100644 index 0000000..700c5dd --- /dev/null +++ b/lldb/test/functionalities/exec/main.cpp @@ -0,0 +1,94 @@ +#include <errno.h> +#include <mach/mach.h> +#include <signal.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <spawn.h> +#include <unistd.h> + +static void +exit_with_errno (int err, const char *prefix) +{ + if (err) + { + fprintf (stderr, + "%s%s", + prefix ? prefix : "", + strerror(err)); + exit (err); + } +} + +static pid_t +spawn_process (const char **argv, + const char **envp, + cpu_type_t cpu_type, + int &err) +{ + pid_t pid = 0; + + const posix_spawn_file_actions_t *file_actions = NULL; + posix_spawnattr_t attr; + err = posix_spawnattr_init (&attr); + if (err) + return pid; + + short flags = POSIX_SPAWN_SETEXEC | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSIGMASK; + err = posix_spawnattr_setflags (&attr, flags); + if (err == 0) + { + // Use the default signal masks + sigset_t no_signals; + sigset_t all_signals; + sigemptyset (&no_signals); + sigfillset (&all_signals); + posix_spawnattr_setsigmask(&attr, &no_signals); + posix_spawnattr_setsigdefault(&attr, &all_signals); + + if (cpu_type != 0) + { + size_t ocount = 0; + err = posix_spawnattr_setbinpref_np (&attr, 1, &cpu_type, &ocount); + } + + if (err == 0) + { + err = posix_spawn (&pid, + argv[0], + file_actions, + &attr, + (char * const *)argv, + (char * const *)envp); + } + + posix_spawnattr_destroy(&attr); + } + return pid; +} + +int +main (int argc, char const **argv) +{ + printf ("pid %i: Pointer size is %zu.\n", getpid(), sizeof(void *)); + int err = 0; // Set breakpoint 1 here +#if defined (__x86_64__) + if (sizeof(void *) == 8) + { + spawn_process (argv, NULL, CPU_TYPE_I386, err); + if (err) + exit_with_errno (err, "posix_spawn i386 error"); + } + else + { + spawn_process (argv, NULL, CPU_TYPE_X86_64, err); + if (err) + exit_with_errno (err, "posix_spawn x86_64 error"); + } +#else + spawn_process (argv, NULL, 0, err); + if (err) + exit_with_errno (err, "posix_spawn x86_64 error"); +#endif + return 0; +} diff --git a/lldb/test/functionalities/fat_archives/Makefile b/lldb/test/functionalities/fat_archives/Makefile new file mode 100644 index 0000000..d22c83f --- /dev/null +++ b/lldb/test/functionalities/fat_archives/Makefile @@ -0,0 +1,12 @@ +all: clean + $(CC) -arch i386 -g -c a.c + ar -q liba-i386.a a.o + $(CC) -arch x86_64 -g -c a.c + ar -q liba-x86_64.a a.o + lipo -create -output liba.a liba-i386.a liba-x86_64.a + $(CC) -g -c main.c + $(CC) -o a.out main.o -L. -la + +clean: + rm -rf a.o a.out liba-i386.a liba-x86_64.a liba.a *un~ .*un~ main.o *.pyc + diff --git a/lldb/test/functionalities/fat_archives/TestFatArchives.py b/lldb/test/functionalities/fat_archives/TestFatArchives.py new file mode 100644 index 0000000..13146ed --- /dev/null +++ b/lldb/test/functionalities/fat_archives/TestFatArchives.py @@ -0,0 +1,63 @@ +""" +Test some lldb command abbreviations. +""" +import commands +import lldb +import os +import time +import unittest2 +from lldbtest import * +import lldbutil + +def execute_command (command): + # print '%% %s' % (command) + (exit_status, output) = commands.getstatusoutput (command) + # if output: + # print output + # print 'status = %u' % (exit_status) + return exit_status + +class FatArchiveTestCase(TestBase): + + mydir = os.path.join("functionalities", "fat_archives") + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @dwarf_test + def test_with_dwarf (self): + if self.getArchitecture() == 'x86_64': + execute_command ("make CC='%s'" % (os.environ["CC"])) + self.main () + else: + self.skipTest("This test requires x86_64 as the architecture for the inferior") + + def main (self): + '''This test compiles a quick example by making a fat file (universal) full of + skinny .o files and makes sure we can use them to resolve breakpoints when doing + DWARF in .o file debugging. The only thing this test needs to do is to compile and + set a breakpoint in the target and verify any breakpoint locations have valid debug + info for the function, and source file and line.''' + exe = os.path.join (os.getcwd(), "a.out") + + # Create the target + target = self.dbg.CreateTarget(exe) + + # Create a breakpoint by name + breakpoint = target.BreakpointCreateByName ('foo', exe) + self.assertTrue(breakpoint, VALID_BREAKPOINT) + + # Make sure the breakpoint resolves to a function, file and line + for bp_loc in breakpoint: + # Get a section offset address (lldb.SBAddress) from the breakpoint location + bp_loc_addr = bp_loc.GetAddress() + line_entry = bp_loc_addr.GetLineEntry() + function = bp_loc_addr.GetFunction() + self.assertTrue(function.IsValid(), "Verify breakpoint in fat BSD archive has valid function debug info") + self.assertTrue(line_entry.GetFileSpec(), "Verify breakpoint in fat BSD archive has source file information") + self.assertTrue(line_entry.GetLine() != 0, "Verify breakpoint in fat BSD archive has source line information") + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() + diff --git a/lldb/test/functionalities/fat_archives/a.c b/lldb/test/functionalities/fat_archives/a.c new file mode 100644 index 0000000..c100f9a --- /dev/null +++ b/lldb/test/functionalities/fat_archives/a.c @@ -0,0 +1,4 @@ +int foo () +{ + return 5; +} diff --git a/lldb/test/functionalities/fat_archives/a.h b/lldb/test/functionalities/fat_archives/a.h new file mode 100644 index 0000000..a453664 --- /dev/null +++ b/lldb/test/functionalities/fat_archives/a.h @@ -0,0 +1 @@ +int foo (); diff --git a/lldb/test/functionalities/fat_archives/main.c b/lldb/test/functionalities/fat_archives/main.c new file mode 100644 index 0000000..328319d4 --- /dev/null +++ b/lldb/test/functionalities/fat_archives/main.c @@ -0,0 +1,6 @@ +#include "a.h" +#include <stdio.h> +int main() +{ + printf ("%d\n", foo()); +} diff --git a/lldb/test/functionalities/inferior-assert/Makefile b/lldb/test/functionalities/inferior-assert/Makefile new file mode 100644 index 0000000..0d70f25 --- /dev/null +++ b/lldb/test/functionalities/inferior-assert/Makefile @@ -0,0 +1,5 @@ +LEVEL = ../../make + +C_SOURCES := main.c + +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/inferior-assert/TestInferiorAssert.py b/lldb/test/functionalities/inferior-assert/TestInferiorAssert.py new file mode 100644 index 0000000..0d4d7b1 --- /dev/null +++ b/lldb/test/functionalities/inferior-assert/TestInferiorAssert.py @@ -0,0 +1,221 @@ +"""Test that lldb functions correctly after the inferior has asserted.""" + +import os, time +import unittest2 +import lldb, lldbutil +from lldbtest import * + +class AssertingInferiorTestCase(TestBase): + + mydir = os.path.join("functionalities", "inferior-assert") + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + def test_inferior_asserting_dsym(self): + """Test that lldb reliably catches the inferior asserting (command).""" + self.buildDsym() + self.inferior_asserting() + + @skipIfGcc # avoid an xpass on the buildbots where libc was not built with -fomit-frame-pointer + @expectedFailureLinux # llvm.org/pr15671 - backtrace does not include the assert site + def test_inferior_asserting_dwarf(self): + """Test that lldb reliably catches the inferior asserting (command).""" + self.buildDwarf() + self.inferior_asserting() + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + def test_inferior_asserting_registers_dsym(self): + """Test that lldb reliably reads registers from the inferior after asserting (command).""" + self.buildDsym() + self.inferior_asserting_registers() + + def test_inferior_asserting_register_dwarf(self): + """Test that lldb reliably reads registers from the inferior after asserting (command).""" + self.buildDwarf() + self.inferior_asserting_registers() + + @python_api_test + def test_inferior_asserting_python(self): + """Test that lldb reliably catches the inferior asserting (Python API).""" + self.buildDefault() + self.inferior_asserting_python() + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + def test_inferior_asserting_expr(self): + """Test that the lldb expression interpreter can read from the inferior after asserting (command).""" + self.buildDsym() + self.inferior_asserting_expr() + + @skipIfGcc # avoid an xpass on the buildbots where libc was not built with -fomit-frame-pointer + @expectedFailureLinux # llvm.org/pr15671 - backtrace does not include the assert site + def test_inferior_asserting_expr(self): + """Test that the lldb expression interpreter can read from the inferior after asserting (command).""" + self.buildDwarf() + self.inferior_asserting_expr() + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + def test_inferior_asserting_step(self): + """Test that lldb functions correctly after stepping through a call to assert().""" + self.buildDsym() + self.inferior_asserting_step() + + @skipIfGcc # avoid an xpass on the buildbots where libc was not built with -fomit-frame-pointer + @expectedFailureLinux # llvm.org/pr15671 - backtrace does not include the assert site + def test_inferior_asserting_step(self): + """Test that lldb functions correctly after stepping through a call to assert().""" + self.buildDwarf() + self.inferior_asserting_step() + + def set_breakpoint(self, line): + lldbutil.run_break_set_by_file_and_line (self, "main.c", line, num_expected_locations=1, loc_exact=True) + + def check_stop_reason(self): + stop_reason = 'stop reason = signal SIGABRT' + + # The stop reason of the thread should be an abort signal or exception. + self.expect("thread list", STOPPED_DUE_TO_ASSERT, + substrs = ['stopped', + stop_reason]) + + return stop_reason + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number of the call to assert. + self.line = line_number('main.c', '// Assert here.') + + def inferior_asserting(self): + """Inferior asserts upon launching; lldb should catch the event and stop.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("run", RUN_SUCCEEDED) + stop_reason = self.check_stop_reason() + + # And it should report a backtrace that includes the assert site. + self.expect("thread backtrace all", + substrs = [stop_reason, 'main', 'argc', 'argv']) + + # And it should report the correct line number. + self.expect("thread backtrace all", + substrs = [stop_reason, + 'main.c:%d' % self.line]) + + def inferior_asserting_python(self): + """Inferior asserts upon launching; lldb should catch the event and stop.""" + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now launch the process, and do not stop at entry point. + # Both argv and envp are null. + process = target.LaunchSimple(None, None, os.getcwd()) + + if process.GetState() != lldb.eStateStopped: + self.fail("Process should be in the 'stopped' state, " + "instead the actual state is: '%s'" % + lldbutil.state_type_to_str(process.GetState())) + + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonSignal) + if not thread: + self.fail("Fail to stop the thread upon assert") + + if self.TraceOn(): + lldbutil.print_stacktrace(thread) + + def inferior_asserting_registers(self): + """Test that lldb can read registers after asserting.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("run", RUN_SUCCEEDED) + self.check_stop_reason() + + # lldb should be able to read from registers from the inferior after asserting. + self.expect("register read eax", + substrs = ['eax = 0x']) + + def check_expr_in_main(self, thread): + depth = thread.GetNumFrames() + for i in range(depth): + frame = thread.GetFrameAtIndex(i) + self.assertTrue(frame.IsValid(), "current frame is valid") + if self.TraceOn(): + print "Checking if function %s is main" % frame.GetFunctionName() + + if 'main' == frame.GetFunctionName(): + frame_id = frame.GetFrameID() + self.runCmd("frame select " + str(frame_id), RUN_SUCCEEDED) + self.expect("p argc", substrs = ['(int)', ' = 1']) + self.expect("p hello_world", substrs = ['Hello']) + self.expect("p argv[0]", substrs = ['a.out']) + self.expect("p null_ptr", substrs = ['= 0x0']) + return True + return False + + def inferior_asserting_expr(self): + """Test that the lldb expression interpreter can read symbols after asserting.""" + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Launch the process, and do not stop at the entry point. + target.LaunchSimple(None, None, os.getcwd()) + self.check_stop_reason() + + process = target.GetProcess() + self.assertTrue(process.IsValid(), "current process is valid") + + thread = process.GetThreadAtIndex(0) + self.assertTrue(thread.IsValid(), "current thread is valid") + + # The lldb expression interpreter should be able to read from addresses of the inferior after a call to assert(). + self.assertTrue(self.check_expr_in_main(thread), "cannot find 'main' in the backtrace") + + def inferior_asserting_step(self): + """Test that lldb functions correctly after stepping through a call to assert().""" + exe = os.path.join(os.getcwd(), "a.out") + + # Create a target by the debugger. + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Launch the process, and do not stop at the entry point. + self.set_breakpoint(self.line) + target.LaunchSimple(None, None, os.getcwd()) + + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['main.c:%d' % self.line, + 'stop reason = breakpoint']) + + self.runCmd("next") + stop_reason = self.check_stop_reason() + + # lldb should be able to read from registers from the inferior after asserting. + if "x86_64" in self.getArchitecture(): + self.expect("register read rbp", substrs = ['rbp = 0x']) + if "i386" in self.getArchitecture(): + self.expect("register read ebp", substrs = ['ebp = 0x']) + + process = target.GetProcess() + self.assertTrue(process.IsValid(), "current process is valid") + + thread = process.GetThreadAtIndex(0) + self.assertTrue(thread.IsValid(), "current thread is valid") + + # The lldb expression interpreter should be able to read from addresses of the inferior after a call to assert(). + self.assertTrue(self.check_expr_in_main(thread), "cannot find 'main' in the backtrace") + + # And it should report the correct line number. + self.expect("thread backtrace all", + substrs = [stop_reason, + 'main.c:%d' % self.line]) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/functionalities/inferior-assert/main.c b/lldb/test/functionalities/inferior-assert/main.c new file mode 100644 index 0000000..6aec4c1 --- /dev/null +++ b/lldb/test/functionalities/inferior-assert/main.c @@ -0,0 +1,19 @@ +//===-- main.c --------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#include <stdio.h> +#include <assert.h> + +const char *hello_world = "Hello, assertion!"; + +int main(int argc, const char* argv[]) +{ + int *null_ptr = 0; + printf("%s\n", hello_world); + assert(null_ptr); // Assert here. +} diff --git a/lldb/test/functionalities/inferior-crashing/TestInferiorCrashing.py b/lldb/test/functionalities/inferior-crashing/TestInferiorCrashing.py index 63326ea..14b941e 100644 --- a/lldb/test/functionalities/inferior-crashing/TestInferiorCrashing.py +++ b/lldb/test/functionalities/inferior-crashing/TestInferiorCrashing.py @@ -86,6 +86,19 @@ class CrashingInferiorTestCase(TestBase): def set_breakpoint(self, line): lldbutil.run_break_set_by_file_and_line (self, "main.c", line, num_expected_locations=1, loc_exact=True) + def check_stop_reason(self): + if sys.platform.startswith("darwin"): + stop_reason = 'stop reason = EXC_BAD_ACCESS' + else: + stop_reason = 'stop reason = invalid address' + + # The stop reason of the thread should be a bad access exception. + self.expect("thread list", STOPPED_DUE_TO_EXC_BAD_ACCESS, + substrs = ['stopped', + stop_reason]) + + return stop_reason + def setUp(self): # Call super's setUp(). TestBase.setUp(self) @@ -98,16 +111,7 @@ class CrashingInferiorTestCase(TestBase): self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) self.runCmd("run", RUN_SUCCEEDED) - - if sys.platform.startswith("darwin"): - stop_reason = 'stop reason = EXC_BAD_ACCESS' - else: - stop_reason = 'stop reason = invalid address' - - # The stop reason of the thread should be a bad access exception. - self.expect("thread list", STOPPED_DUE_TO_EXC_BAD_ACCESS, - substrs = ['stopped', - stop_reason]) + stop_reason = self.check_stop_reason() # And it should report the correct line number. self.expect("thread backtrace all", @@ -125,7 +129,6 @@ class CrashingInferiorTestCase(TestBase): # Both argv and envp are null. process = target.LaunchSimple(None, None, os.getcwd()) - import lldbutil if process.GetState() != lldb.eStateStopped: self.fail("Process should be in the 'stopped' state, " "instead the actual state is: '%s'" % @@ -144,11 +147,7 @@ class CrashingInferiorTestCase(TestBase): self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) self.runCmd("run", RUN_SUCCEEDED) - - if sys.platform.startswith("darwin"): - stop_reason = 'stop reason = EXC_BAD_ACCESS' - else: - stop_reason = 'stop reason = invalid address' + self.check_stop_reason() # lldb should be able to read from registers from the inferior after crashing. self.expect("register read eax", @@ -160,15 +159,7 @@ class CrashingInferiorTestCase(TestBase): self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) self.runCmd("run", RUN_SUCCEEDED) - - if sys.platform.startswith("darwin"): - stop_reason = 'stop reason = EXC_BAD_ACCESS' - else: - stop_reason = 'stop reason = invalid address' - - # The stop reason of the thread should be a bad access exception. - self.expect("thread list", STOPPED_DUE_TO_EXC_BAD_ACCESS, - substrs = ['stopped', stop_reason]) + self.check_stop_reason() # The lldb expression interpreter should be able to read from addresses of the inferior after a crash. self.expect("p argc", @@ -190,15 +181,7 @@ class CrashingInferiorTestCase(TestBase): 'stop reason = breakpoint']) self.runCmd("next") - - if sys.platform.startswith("darwin"): - stop_reason = 'stop reason = EXC_BAD_ACCESS' - else: - stop_reason = 'stop reason = invalid address' - - # The stop reason of the thread should be a bad access exception. - self.expect("thread list", STOPPED_DUE_TO_EXC_BAD_ACCESS, - substrs = ['stopped', stop_reason]) + stop_reason = self.check_stop_reason() # The lldb expression interpreter should be able to read from addresses of the inferior after a crash. self.expect("p argv[0]", @@ -221,17 +204,10 @@ class CrashingInferiorTestCase(TestBase): self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) self.runCmd("run", RUN_SUCCEEDED) - - if sys.platform.startswith("darwin"): - stop_reason = 'stop reason = EXC_BAD_ACCESS' - else: - stop_reason = 'stop reason = invalid address' + self.check_stop_reason() self.runCmd("next") - - # The stop reason of the thread should still be a bad access exception. - self.expect("thread list", STOPPED_DUE_TO_EXC_BAD_ACCESS, - substrs = ['stopped', stop_reason]) + self.check_stop_reason() def inferior_crashing_expr_step_expr(self): """Test that lldb expressions work before and after stepping after a crash.""" @@ -239,22 +215,14 @@ class CrashingInferiorTestCase(TestBase): self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) self.runCmd("run", RUN_SUCCEEDED) - - if sys.platform.startswith("darwin"): - stop_reason = 'stop reason = EXC_BAD_ACCESS' - else: - stop_reason = 'stop reason = invalid address' + self.check_stop_reason() # The lldb expression interpreter should be able to read from addresses of the inferior after a crash. self.expect("p argv[0]", substrs = ['a.out']) self.runCmd("next") - - if sys.platform.startswith("darwin"): - stop_reason = 'stop reason = EXC_BAD_ACCESS' - else: - stop_reason = 'stop reason = invalid address' + self.check_stop_reason() # The lldb expression interpreter should be able to read from addresses of the inferior after a crash. self.expect("p argv[0]", diff --git a/lldb/test/functionalities/inferior-crashing/recursive-inferior/Makefile b/lldb/test/functionalities/inferior-crashing/recursive-inferior/Makefile new file mode 100644 index 0000000..0f8e92e --- /dev/null +++ b/lldb/test/functionalities/inferior-crashing/recursive-inferior/Makefile @@ -0,0 +1,7 @@ +LEVEL = ../../../make + +C_SOURCES := main.c + +CFLAGS_EXTRAS += -fomit-frame-pointer + +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/inferior-crashing/recursive-inferior/TestRecursiveInferior.py b/lldb/test/functionalities/inferior-crashing/recursive-inferior/TestRecursiveInferior.py new file mode 100644 index 0000000..006da45 --- /dev/null +++ b/lldb/test/functionalities/inferior-crashing/recursive-inferior/TestRecursiveInferior.py @@ -0,0 +1,242 @@ +"""Test that lldb functions correctly after the inferior has crashed while in a recursive routine.""" + +import os, time +import unittest2 +import lldb, lldbutil +from lldbtest import * + +class CrashingRecursiveInferiorTestCase(TestBase): + + mydir = os.path.join("functionalities", "inferior-crashing", "recursive-inferior") + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + def test_recursive_inferior_crashing_dsym(self): + """Test that lldb reliably catches the inferior crashing (command).""" + self.buildDsym() + self.recursive_inferior_crashing() + + @expectedFailureLinux('llvm.org/pr15415', ['gcc', 'clang']) # partial backtrace with -fomit-frame-pointer with tool-chains that support this option + def test_recursive_inferior_crashing_dwarf(self): + """Test that lldb reliably catches the inferior crashing (command).""" + self.buildDwarf() + self.recursive_inferior_crashing() + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + def test_recursive_inferior_crashing_registers_dsym(self): + """Test that lldb reliably reads registers from the inferior after crashing (command).""" + self.buildDsym() + self.recursive_inferior_crashing_registers() + + def test_recursive_inferior_crashing_register_dwarf(self): + """Test that lldb reliably reads registers from the inferior after crashing (command).""" + self.buildDwarf() + self.recursive_inferior_crashing_registers() + + @python_api_test + def test_recursive_inferior_crashing_python(self): + """Test that lldb reliably catches the inferior crashing (Python API).""" + self.buildDefault() + self.recursive_inferior_crashing_python() + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + def test_recursive_inferior_crashing_expr_dsym(self): + """Test that the lldb expression interpreter can read from the inferior after crashing (command).""" + self.buildDsym() + self.recursive_inferior_crashing_expr() + + def test_recursive_inferior_crashing_expr_dwarf(self): + """Test that the lldb expression interpreter can read from the inferior after crashing (command).""" + self.buildDwarf() + self.recursive_inferior_crashing_expr() + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + def test_recursive_inferior_crashing_step_dsym(self): + """Test that lldb functions correctly after stepping through a crash.""" + self.buildDsym() + self.recursive_inferior_crashing_step() + + def test_recursive_inferior_crashing_step_dwarf(self): + """Test that stepping after a crash behaves correctly.""" + self.buildDwarf() + self.recursive_inferior_crashing_step() + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + def test_recursive_inferior_crashing_step_after_break_dsym(self): + """Test that stepping after a crash behaves correctly.""" + self.buildDsym() + self.recursive_inferior_crashing_step_after_break() + + def test_recursive_inferior_crashing_step_after_break_dwarf(self): + """Test that lldb functions correctly after stepping through a crash.""" + self.buildDwarf() + self.recursive_inferior_crashing_step_after_break() + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + def test_recursive_inferior_crashing_expr_step_and_expr_dsym(self): + """Test that lldb expressions work before and after stepping after a crash.""" + self.buildDsym() + self.recursive_inferior_crashing_expr_step_expr() + + @expectedFailureLinux # due to llvm.org/pr15415 with -fomit-frame-pointer, and pr15989 with ebp/rbp + def test_recursive_inferior_crashing_expr_step_and_expr_dwarf(self): + """Test that lldb expressions work before and after stepping after a crash.""" + self.buildDwarf() + self.recursive_inferior_crashing_expr_step_expr() + + def set_breakpoint(self, line): + lldbutil.run_break_set_by_file_and_line (self, "main.c", line, num_expected_locations=1, loc_exact=True) + + def check_stop_reason(self): + if sys.platform.startswith("darwin"): + stop_reason = 'stop reason = EXC_BAD_ACCESS' + else: + stop_reason = 'stop reason = invalid address' + + # The stop reason of the thread should be a bad access exception. + self.expect("thread list", STOPPED_DUE_TO_EXC_BAD_ACCESS, + substrs = ['stopped', + stop_reason]) + + return stop_reason + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number of the crash. + self.line = line_number('main.c', '// Crash here.') + + def recursive_inferior_crashing(self): + """Inferior crashes upon launching; lldb should catch the event and stop.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("run", RUN_SUCCEEDED) + stop_reason = self.check_stop_reason() + + # And it should report a backtrace that includes main and the crash site. + self.expect("thread backtrace all", + substrs = [stop_reason, 'main', 'argc', 'argv', 'recursive_function']) + + # And it should report the correct line number. + self.expect("thread backtrace all", + substrs = [stop_reason, + 'main.c:%d' % self.line]) + + def recursive_inferior_crashing_python(self): + """Inferior crashes upon launching; lldb should catch the event and stop.""" + exe = os.path.join(os.getcwd(), "a.out") + + target = self.dbg.CreateTarget(exe) + self.assertTrue(target, VALID_TARGET) + + # Now launch the process, and do not stop at entry point. + # Both argv and envp are null. + process = target.LaunchSimple(None, None, os.getcwd()) + + if process.GetState() != lldb.eStateStopped: + self.fail("Process should be in the 'stopped' state, " + "instead the actual state is: '%s'" % + lldbutil.state_type_to_str(process.GetState())) + + thread = lldbutil.get_stopped_thread(process, lldb.eStopReasonException) + if not thread: + self.fail("Fail to stop the thread upon bad access exception") + + if self.TraceOn(): + lldbutil.print_stacktrace(thread) + + def recursive_inferior_crashing_registers(self): + """Test that lldb can read registers after crashing.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("run", RUN_SUCCEEDED) + self.check_stop_reason() + + # lldb should be able to read from registers from the inferior after crashing. + self.expect("register read eax", + substrs = ['eax = 0x']) + + def recursive_inferior_crashing_expr(self): + """Test that the lldb expression interpreter can read symbols after crashing.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("run", RUN_SUCCEEDED) + self.check_stop_reason() + + # The lldb expression interpreter should be able to read from addresses of the inferior after a crash. + self.expect("p i", + startstr = '(int) $0 =') + + def recursive_inferior_crashing_step(self): + """Test that lldb functions correctly after stepping through a crash.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.set_breakpoint(self.line) + self.runCmd("run", RUN_SUCCEEDED) + + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['main.c:%d' % self.line, + 'stop reason = breakpoint']) + + self.runCmd("next") + stop_reason = self.check_stop_reason() + + # The lldb expression interpreter should be able to read from addresses of the inferior after a crash. + self.expect("p i", + substrs = ['(int) $0 =']) + + # lldb should be able to read from registers from the inferior after crashing. + self.expect("register read eax", + substrs = ['eax = 0x']) + + # And it should report the correct line number. + self.expect("thread backtrace all", + substrs = [stop_reason, + 'main.c:%d' % self.line]) + + def recursive_inferior_crashing_step_after_break(self): + """Test that lldb behaves correctly when stepping after a crash.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("run", RUN_SUCCEEDED) + self.check_stop_reason() + + expected_state = 'exited' # Provide the exit code. + if sys.platform.startswith("darwin"): + expected_state = 'stopped' # TODO: Determine why 'next' and 'continue' have no effect after a crash. + + self.expect("next", + substrs = ['Process', expected_state]) + + self.expect("thread list", error=True, + substrs = ['Process must be launched']) + + def recursive_inferior_crashing_expr_step_expr(self): + """Test that lldb expressions work before and after stepping after a crash.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + self.runCmd("run", RUN_SUCCEEDED) + self.check_stop_reason() + + # The lldb expression interpreter should be able to read from addresses of the inferior after a crash. + self.expect("p null", + startstr = '(char *) $0 = 0x0') + + self.runCmd("next") + + # The lldb expression interpreter should be able to read from addresses of the inferior after a step. + self.expect("p null", + startstr = '(char *) $1 = 0x0') + + self.check_stop_reason() + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/functionalities/inferior-crashing/recursive-inferior/main.c b/lldb/test/functionalities/inferior-crashing/recursive-inferior/main.c new file mode 100644 index 0000000..25e6e8d --- /dev/null +++ b/lldb/test/functionalities/inferior-crashing/recursive-inferior/main.c @@ -0,0 +1,19 @@ +void recursive_function(int i) +{ + if (i < 10) + { + recursive_function(i + 1); + } + else + { + char *null=0; + *null = 0; // Crash here. + } +} + +int main(int argc, char *argv[]) +{ + recursive_function(0); + return 0; +} + diff --git a/lldb/test/functionalities/platform/TestPlatformCommand.py b/lldb/test/functionalities/platform/TestPlatformCommand.py index 8aab0e5..dbdebea 100644 --- a/lldb/test/functionalities/platform/TestPlatformCommand.py +++ b/lldb/test/functionalities/platform/TestPlatformCommand.py @@ -27,7 +27,6 @@ class PlatformCommandTestCase(TestBase): self.expect("platform process info", error=True, substrs = ['one or more process id(s) must be specified']) - @expectedFailureLinux # due to llvm.org/pr14806 -- "platform status" prints more information on Mac OS X than on Linux def test_status(self): self.expect("platform status", substrs = ['Platform', 'Triple', 'OS Version', 'Kernel', 'Hostname']) diff --git a/lldb/test/functionalities/register/TestRegisters.py b/lldb/test/functionalities/register/TestRegisters.py index d3b8d0d..06da031 100644 --- a/lldb/test/functionalities/register/TestRegisters.py +++ b/lldb/test/functionalities/register/TestRegisters.py @@ -55,7 +55,6 @@ class RegisterCommandsTestCase(TestBase): def common_setup(self): exe = os.path.join(os.getcwd(), "a.out") - self.log_file = exe + ".log" self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) @@ -77,10 +76,11 @@ class RegisterCommandsTestCase(TestBase): self.platform = "linux" if self.platform != "": - self.runCmd("log enable " + self.platform + " " + str(category) + " registers -v -f " + self.log_file, RUN_SUCCEEDED) + log_file = os.path.join(os.getcwd(), 'TestRegisters.log') + self.runCmd("log enable " + self.platform + " " + str(category) + " registers -v -f " + log_file, RUN_SUCCEEDED) if not self.has_teardown: self.has_teardown = True - self.addTearDownHook(lambda: os.remove(self.log_file)) + self.addTearDownHook(lambda: os.remove(log_file)) def register_commands(self): """Test commands related to registers, in particular vector registers.""" diff --git a/lldb/test/functionalities/thread/break_after_join/Makefile b/lldb/test/functionalities/thread/break_after_join/Makefile new file mode 100644 index 0000000..26db481 --- /dev/null +++ b/lldb/test/functionalities/thread/break_after_join/Makefile @@ -0,0 +1,4 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/thread/break_after_join/TestBreakAfterJoin.py b/lldb/test/functionalities/thread/break_after_join/TestBreakAfterJoin.py new file mode 100644 index 0000000..2c7f7b1 --- /dev/null +++ b/lldb/test/functionalities/thread/break_after_join/TestBreakAfterJoin.py @@ -0,0 +1,102 @@ +""" +Test number of threads. +""" + +import os, time +import unittest2 +import lldb +from lldbtest import * +import lldbutil + +class BreakpointAfterJoinTestCase(TestBase): + + mydir = os.path.join("functionalities", "thread", "break_after_join") + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @dsym_test + def test_with_dsym(self): + """Test breakpoint handling after a thread join.""" + self.buildDsym(dictionary=self.getBuildFlags()) + self.breakpoint_after_join_test() + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @skipIfLinux # Causes hangs (llvm.org/pr16170) when run using "make check" + @dwarf_test + def test_with_dwarf(self): + """Test breakpoint handling after a thread join.""" + self.buildDwarf(dictionary=self.getBuildFlags()) + self.breakpoint_after_join_test() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number for our breakpoint. + self.breakpoint = line_number('main.cpp', '// Set breakpoint here') + + def breakpoint_after_join_test(self): + """Test breakpoint handling after a thread join.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.breakpoint, num_expected_locations=1) + + # The breakpoint list should show 1 location. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file ='main.cpp', line = %d, locations = 1" % self.breakpoint]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # The exit probably occured during breakpoint handling, but it isn't + # guaranteed. The main thing we're testing here is that the debugger + # handles this cleanly is some way. + + # Get the number of threads + num_threads = process.GetNumThreads() + + # Make sure we see six threads + self.assertTrue(num_threads == 6, 'Number of expected threads and actual threads do not match.') + + # Get the thread objects + thread1 = process.GetThreadAtIndex(0) + thread2 = process.GetThreadAtIndex(1) + thread3 = process.GetThreadAtIndex(2) + thread4 = process.GetThreadAtIndex(3) + thread5 = process.GetThreadAtIndex(4) + thread6 = process.GetThreadAtIndex(5) + + # Make sure all threads are stopped + self.assertTrue(thread1.IsStopped(), "Thread 1 didn't stop during breakpoint") + self.assertTrue(thread2.IsStopped(), "Thread 2 didn't stop during breakpoint") + self.assertTrue(thread3.IsStopped(), "Thread 3 didn't stop during breakpoint") + self.assertTrue(thread4.IsStopped(), "Thread 4 didn't stop during breakpoint") + self.assertTrue(thread5.IsStopped(), "Thread 5 didn't stop during breakpoint") + self.assertTrue(thread6.IsStopped(), "Thread 6 didn't stop during breakpoint") + + # Run to completion + self.runCmd("continue") + + # If the process hasn't exited, collect some information + if process.GetState() != lldb.eStateExited: + self.runCmd("thread list") + self.runCmd("process status") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/functionalities/thread/break_after_join/main.cpp b/lldb/test/functionalities/thread/break_after_join/main.cpp new file mode 100644 index 0000000..9764976 --- /dev/null +++ b/lldb/test/functionalities/thread/break_after_join/main.cpp @@ -0,0 +1,125 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test is intended to create a situation in which one thread will exit +// while a breakpoint is being handled in another thread. This may not always +// happen because it's possible that the exiting thread will exit before the +// breakpoint is hit. The test case should be flexible enough to treat that +// as success. + +#include <pthread.h> +#include <unistd.h> +#include <atomic> + +volatile int g_test = 0; + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +// A barrier to synchronize all the threads. +std::atomic_int g_barrier1; + +// A barrier to keep the threads from exiting until after the breakpoint has +// been passed. +std::atomic_int g_barrier2; + +void * +break_thread_func (void *input) +{ + // Wait until all the threads are running + pseudo_barrier_wait(g_barrier1); + + // Wait for the join thread to join + usleep(50); + + // Do something + g_test++; // Set breakpoint here + + // Synchronize after the breakpoint + pseudo_barrier_wait(g_barrier2); + + // Return + return NULL; +} + +void * +wait_thread_func (void *input) +{ + // Wait until the entire first group of threads is running + pseudo_barrier_wait(g_barrier1); + + // Wait until the breakpoint has been passed + pseudo_barrier_wait(g_barrier2); + + // Return + return NULL; +} + +void * +join_thread_func (void *input) +{ + pthread_t *thread_to_join = (pthread_t*)input; + + // Sync up with the rest of the threads. + pseudo_barrier_wait(g_barrier1); + + // Join the other thread + pthread_join(*thread_to_join, NULL); + + // Return + return NULL; +} + +int main () +{ + pthread_t thread_1; + pthread_t thread_2; + pthread_t thread_3; + pthread_t thread_4; + pthread_t thread_5; + + // The first barrier waits for the non-joining threads to start. + // This thread will also participate in that barrier. + // The idea here is to guarantee that the joining thread will be + // last in the internal list maintained by the debugger. + pseudo_barrier_init(g_barrier1, 5); + + // The second barrier keeps the waiting threads around until the breakpoint + // has been passed. + pseudo_barrier_init(g_barrier2, 4); + + // Create a thread to hit the breakpoint + pthread_create (&thread_1, NULL, break_thread_func, NULL); + + // Create more threads to slow the debugger down during processing. + pthread_create (&thread_2, NULL, wait_thread_func, NULL); + pthread_create (&thread_3, NULL, wait_thread_func, NULL); + pthread_create (&thread_4, NULL, wait_thread_func, NULL); + + // Create a thread to join the breakpoint thread + pthread_create (&thread_5, NULL, join_thread_func, &thread_4); + + // Wait for the threads to finish + pthread_join(thread_5, NULL); + pthread_join(thread_4, NULL); + pthread_join(thread_3, NULL); + pthread_join(thread_2, NULL); + pthread_join(thread_1, NULL); + + return 0; +} diff --git a/lldb/test/functionalities/thread/create_after_attach/Makefile b/lldb/test/functionalities/thread/create_after_attach/Makefile new file mode 100644 index 0000000..3b5a8a3 --- /dev/null +++ b/lldb/test/functionalities/thread/create_after_attach/Makefile @@ -0,0 +1,4 @@ +LEVEL = ../../../make + +C_SOURCES := main.c +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/thread/create_after_attach/TestCreateAfterAttach.py b/lldb/test/functionalities/thread/create_after_attach/TestCreateAfterAttach.py new file mode 100644 index 0000000..082922c --- /dev/null +++ b/lldb/test/functionalities/thread/create_after_attach/TestCreateAfterAttach.py @@ -0,0 +1,124 @@ +""" +Test thread creation after process attach. +""" + +import os, time +import unittest2 +import lldb +from lldbtest import * +import lldbutil + +class CreateAfterAttachTestCase(TestBase): + + mydir = os.path.join("functionalities", "thread", "create_after_attach") + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @dsym_test + def test_create_after_attach_with_dsym(self): + """Test thread creation after process attach.""" + self.buildDsym(dictionary=self.getBuildFlags(use_cpp11=False)) + self.create_after_attach(use_fork=False) + + @skipIfLinux # Hangs, see llvm.org/pr16229 + @dwarf_test + def test_create_after_attach_with_dwarf_and_popen(self): + """Test thread creation after process attach.""" + self.buildDwarf(dictionary=self.getBuildFlags(use_cpp11=False)) + self.create_after_attach(use_fork=False) + + @dwarf_test + def test_create_after_attach_with_dwarf_and_fork(self): + """Test thread creation after process attach.""" + self.buildDwarf(dictionary=self.getBuildFlags(use_cpp11=False)) + self.create_after_attach(use_fork=True) + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers for our breakpoints. + self.break_1 = line_number('main.c', '// Set first breakpoint here') + self.break_2 = line_number('main.c', '// Set second breakpoint here') + self.break_3 = line_number('main.c', '// Set third breakpoint here') + + def create_after_attach(self, use_fork): + """Test thread creation after process attach.""" + + exe = os.path.join(os.getcwd(), "a.out") + + # Spawn a new process + if use_fork: + pid = self.forkSubprocess(exe) + else: + popen = self.spawnSubprocess(exe) + pid = popen.pid + self.addTearDownHook(self.cleanupSubprocesses) + + # Attach to the spawned process + self.runCmd("process attach -p " + str(pid)) + + target = self.dbg.GetSelectedTarget() + + process = target.GetProcess() + self.assertTrue(process, PROCESS_IS_VALID) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.c", self.break_1, num_expected_locations=1) + + # This should create a breakpoint in the second child thread. + lldbutil.run_break_set_by_file_and_line (self, "main.c", self.break_2, num_expected_locations=1) + + # This should create a breakpoint in the first child thread. + lldbutil.run_break_set_by_file_and_line (self, "main.c", self.break_3, num_expected_locations=1) + + # Run to the first breakpoint + self.runCmd("continue") + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + '* thread #1', + 'stop reason = breakpoint', + 'thread #2']) + + # Change a variable to escape the loop + self.runCmd("expression main_thread_continue = 1") + + # Run to the second breakpoint + self.runCmd("continue") + self.runCmd("thread select 3") + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'thread #1', + 'thread #2', + '* thread #3', + 'stop reason = breakpoint']) + + # Change a variable to escape the loop + self.runCmd("expression child_thread_continue = 1") + + # Run to the third breakpoint + self.runCmd("continue") + self.runCmd("thread select 2") + + # The stop reason of the thread should be breakpoint. + # Thread 3 may or may not have already exited. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'thread #1', + '* thread #2', + 'stop reason = breakpoint']) + + # Run to completion + self.runCmd("continue") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) + + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/functionalities/thread/create_after_attach/main.c b/lldb/test/functionalities/thread/create_after_attach/main.c new file mode 100644 index 0000000..6834026 --- /dev/null +++ b/lldb/test/functionalities/thread/create_after_attach/main.c @@ -0,0 +1,60 @@ +#include <stdio.h> +#include <unistd.h> +#include <pthread.h> + +volatile int g_thread_2_continuing = 0; + +void * +thread_1_func (void *input) +{ + // Waiting to be released by the debugger. + while (!g_thread_2_continuing) // The debugger will change this value + { + usleep(1); + } + + // Return + return NULL; // Set third breakpoint here +} + +void * +thread_2_func (void *input) +{ + // Waiting to be released by the debugger. + int child_thread_continue = 0; + while (!child_thread_continue) // The debugger will change this value + { + usleep(1); // Set second breakpoint here + } + + // Release thread 1 + g_thread_2_continuing = 1; + + // Return + return NULL; +} + +int main(int argc, char const *argv[]) +{ + pthread_t thread_1; + pthread_t thread_2; + + // Create a new thread + pthread_create (&thread_1, NULL, thread_1_func, NULL); + + // Waiting to be attached by the debugger. + int main_thread_continue = 0; + while (!main_thread_continue) // The debugger will change this value + { + usleep(1); // Set first breakpoint here + } + + // Create another new thread + pthread_create (&thread_2, NULL, thread_2_func, NULL); + + // Wait for the threads to finish. + pthread_join(thread_1, NULL); + pthread_join(thread_2, NULL); + + printf("Exiting now\n"); +} diff --git a/lldb/test/functionalities/thread/create_during_step/Makefile b/lldb/test/functionalities/thread/create_during_step/Makefile new file mode 100644 index 0000000..26db481 --- /dev/null +++ b/lldb/test/functionalities/thread/create_during_step/Makefile @@ -0,0 +1,4 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/thread/create_during_step/TestCreateDuringStep.py b/lldb/test/functionalities/thread/create_during_step/TestCreateDuringStep.py new file mode 100644 index 0000000..23a777c --- /dev/null +++ b/lldb/test/functionalities/thread/create_during_step/TestCreateDuringStep.py @@ -0,0 +1,155 @@ +""" +Test number of threads. +""" + +import os, time +import unittest2 +import lldb +from lldbtest import * +import lldbutil + +class CreateDuringStepTestCase(TestBase): + + mydir = os.path.join("functionalities", "thread", "create_during_step") + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @dsym_test + def test_step_inst_with_dsym(self): + """Test thread creation during step-inst handling.""" + self.buildDsym(dictionary=self.getBuildFlags()) + self.create_during_step_inst_test() + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @dsym_test + def test_step_over_with_dsym(self): + """Test thread creation during step-over handling.""" + self.buildDsym(dictionary=self.getBuildFlags()) + self.create_during_step_over_test() + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @dsym_test + def test_step_in_with_dsym(self): + """Test thread creation during step-in handling.""" + self.buildDsym(dictionary=self.getBuildFlags()) + self.create_during_step_in_test() + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @dwarf_test + def test_step_inst_with_dwarf(self): + """Test thread creation during step-inst handling.""" + self.buildDwarf(dictionary=self.getBuildFlags()) + self.create_during_step_inst_test() + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @dwarf_test + def test_step_over_with_dwarf(self): + """Test thread creation during step-over handling.""" + self.buildDwarf(dictionary=self.getBuildFlags()) + self.create_during_step_over_test() + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @dwarf_test + def test_step_in_with_dwarf(self): + """Test thread creation during step-in handling.""" + self.buildDwarf(dictionary=self.getBuildFlags()) + self.create_during_step_in_test() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break and continue. + self.breakpoint = line_number('main.cpp', '// Set breakpoint here') + self.continuepoint = line_number('main.cpp', '// Continue from here') + + def create_during_step_inst_test(self): + """Test thread creation while using step-inst.""" + self.create_during_step_base("thread step-inst -m all-threads", 'stop reason = instruction step') + + def create_during_step_over_test(self): + """Test thread creation while using step-over.""" + self.create_during_step_base("thread step-over -m all-threads", 'stop reason = step over') + + def create_during_step_in_test(self): + """Test thread creation while using step-in.""" + self.create_during_step_base("thread step-in -m all-threads", 'stop reason = step in') + + def create_during_step_base(self, step_cmd, step_stop_reason): + """Test thread creation while using step-in.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the stepping thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.breakpoint, num_expected_locations=1) + + # The breakpoint list should show 1 location. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file ='main.cpp', line = %d, locations = 1" % self.breakpoint]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + # Make sure we see only two threads + self.assertTrue(num_threads == 2, 'Number of expected threads and actual threads do not match.') + + # Get the thread objects + thread1 = process.GetThreadAtIndex(0) + thread2 = process.GetThreadAtIndex(1) + + # Make sure both threads are stopped + self.assertTrue(thread1.IsStopped(), "Thread 1 didn't stop during breakpoint") + self.assertTrue(thread2.IsStopped(), "Thread 2 didn't stop during breakpoint") + + # Keep stepping until we've reached our designated continue point + stepping_thread = process.GetSelectedThread() + current_line = self.breakpoint + while current_line != self.continuepoint: + self.runCmd(step_cmd) + + # The thread creation may change the selected thread. + # If it does, we just change it back here. + if stepping_thread != process.GetSelectedThread(): + process.SetSelectedThread(stepping_thread) + + frame = stepping_thread.GetFrameAtIndex(0) + current_line = frame.GetLineEntry().GetLine() + + # Make sure we're still where we thought we were + self.assertTrue(current_line >= self.breakpoint, "Stepped to unexpected line, " + str(current_line)) + self.assertTrue(current_line <= self.continuepoint, "Stepped to unexpected line, " + str(current_line)) + + # Update the number of threads + num_threads = process.GetNumThreads() + + # Check to see that we increased the number of threads as expected + self.assertTrue(num_threads == 3, 'Number of expected threads and actual threads do not match after thread exit.') + + self.expect("thread list", 'Process state is stopped due to step', + substrs = ['stopped', + step_stop_reason]) + + # Run to completion + self.runCmd("process continue") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/functionalities/thread/create_during_step/main.cpp b/lldb/test/functionalities/thread/create_during_step/main.cpp new file mode 100644 index 0000000..f64831b --- /dev/null +++ b/lldb/test/functionalities/thread/create_during_step/main.cpp @@ -0,0 +1,92 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test is intended to create a situation in which one thread will be +// created while a the debugger is stepping in another thread. + +#include <pthread.h> +#include <atomic> + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +std::atomic_int g_barrier; + +volatile int g_thread_created = 0; +volatile int g_test = 0; + +void * +step_thread_func (void *input) +{ + g_test = 0; // Set breakpoint here + + while (!g_thread_created) + g_test++; + + // One more time to provide a continue point + g_test++; // Continue from here + + // Return + return NULL; +} + +void * +create_thread_func (void *input) +{ + pthread_t *step_thread = (pthread_t*)input; + + // Wait until the main thread knows this thread is started. + pseudo_barrier_wait(g_barrier); + + // Wait until the other thread is done. + pthread_join(*step_thread, NULL); + + // Return + return NULL; +} + +int main () +{ + pthread_t thread_1; + pthread_t thread_2; + + // Use a simple count to simulate a barrier. + pseudo_barrier_init(g_barrier, 2); + + // Create a thread to hit the breakpoint. + pthread_create (&thread_1, NULL, step_thread_func, NULL); + + // Wait until the step thread is stepping + while (g_test < 1) + do_nothing(); + + // Create a thread to exit while we're stepping. + pthread_create (&thread_2, NULL, create_thread_func, &thread_1); + + // Wait until that thread is started + pseudo_barrier_wait(g_barrier); + + // Let the stepping thread know the other thread is there + g_thread_created = 1; + + // Wait for the threads to finish. + pthread_join(thread_2, NULL); + pthread_join(thread_1, NULL); + + return 0; +} diff --git a/lldb/test/functionalities/thread/exit_during_break/Makefile b/lldb/test/functionalities/thread/exit_during_break/Makefile new file mode 100644 index 0000000..26db481 --- /dev/null +++ b/lldb/test/functionalities/thread/exit_during_break/Makefile @@ -0,0 +1,4 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/thread/exit_during_break/TestExitDuringBreak.py b/lldb/test/functionalities/thread/exit_during_break/TestExitDuringBreak.py new file mode 100644 index 0000000..7c0a29b --- /dev/null +++ b/lldb/test/functionalities/thread/exit_during_break/TestExitDuringBreak.py @@ -0,0 +1,94 @@ +""" +Test number of threads. +""" + +import os, time +import unittest2 +import lldb +from lldbtest import * +import lldbutil + +class ExitDuringBreakpointTestCase(TestBase): + + mydir = os.path.join("functionalities", "thread", "exit_during_break") + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @dsym_test + def test_with_dsym(self): + """Test thread exit during breakpoint handling.""" + self.buildDsym(dictionary=self.getBuildFlags()) + self.exit_during_breakpoint_test() + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @dwarf_test + def test_with_dwarf(self): + """Test thread exit during breakpoint handling.""" + self.buildDwarf(dictionary=self.getBuildFlags()) + self.exit_during_breakpoint_test() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number for our breakpoint. + self.breakpoint = line_number('main.cpp', '// Set breakpoint here') + + def exit_during_breakpoint_test(self): + """Test thread exit during breakpoint handling.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.breakpoint, num_expected_locations=1) + + # The breakpoint list should show 1 location. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file ='main.cpp', line = %d, locations = 1" % self.breakpoint]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # The exit probably occured during breakpoint handling, but it isn't + # guaranteed. The main thing we're testing here is that the debugger + # handles this cleanly is some way. + + # Get the number of threads + num_threads = process.GetNumThreads() + + # Make sure we see at least five threads + self.assertTrue(num_threads >= 5, 'Number of expected threads and actual threads do not match.') + + # Get the thread objects + thread1 = process.GetThreadAtIndex(0) + thread2 = process.GetThreadAtIndex(1) + thread3 = process.GetThreadAtIndex(2) + thread4 = process.GetThreadAtIndex(3) + thread5 = process.GetThreadAtIndex(4) + + # Make sure all threads are stopped + self.assertTrue(thread1.IsStopped(), "Thread 1 didn't stop during breakpoint") + self.assertTrue(thread2.IsStopped(), "Thread 2 didn't stop during breakpoint") + self.assertTrue(thread3.IsStopped(), "Thread 3 didn't stop during breakpoint") + self.assertTrue(thread4.IsStopped(), "Thread 4 didn't stop during breakpoint") + self.assertTrue(thread5.IsStopped(), "Thread 5 didn't stop during breakpoint") + + # Run to completion + self.runCmd("continue") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/functionalities/thread/exit_during_break/main.cpp b/lldb/test/functionalities/thread/exit_during_break/main.cpp new file mode 100644 index 0000000..f900194 --- /dev/null +++ b/lldb/test/functionalities/thread/exit_during_break/main.cpp @@ -0,0 +1,135 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test is intended to create a situation in which one thread will exit +// while a breakpoint is being handled in another thread. This may not always +// happen because it's possible that the exiting thread will exit before the +// breakpoint is hit. The test case should be flexible enough to treat that +// as success. + +#include <pthread.h> +#include <unistd.h> +#include <atomic> + +volatile int g_test = 0; + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +// A barrier to synchronize all the threads except the one that will exit. +std::atomic_int g_barrier1; + +// A barrier to synchronize all the threads including the one that will exit. +std::atomic_int g_barrier2; + +// A barrier to keep the first group of threads from exiting until after the +// breakpoint has been passed. +std::atomic_int g_barrier3; + +void * +break_thread_func (void *input) +{ + // Wait until the entire first group of threads is running + pseudo_barrier_wait(g_barrier1); + + // Wait for the exiting thread to start + pseudo_barrier_wait(g_barrier2); + + // Do something + g_test++; // Set breakpoint here + + // Synchronize after the breakpoint + pseudo_barrier_wait(g_barrier3); + + // Return + return NULL; +} + +void * +wait_thread_func (void *input) +{ + // Wait until the entire first group of threads is running + pseudo_barrier_wait(g_barrier1); + + // Wait for the exiting thread to start + pseudo_barrier_wait(g_barrier2); + + // Wait until the breakpoint has been passed + pseudo_barrier_wait(g_barrier3); + + // Return + return NULL; +} + +void * +exit_thread_func (void *input) +{ + // Sync up with the rest of the threads. + pseudo_barrier_wait(g_barrier2); + + // Try to make sure this thread doesn't exit until the breakpoint is hit. + usleep(1); + + // Return + return NULL; +} + +int main () +{ + pthread_t thread_1; + pthread_t thread_2; + pthread_t thread_3; + pthread_t thread_4; + pthread_t thread_5; + + // The first barrier waits for the non-exiting threads to start. + // This thread will also participate in that barrier. + // The idea here is to guarantee that the exiting thread will be + // last in the internal list maintained by the debugger. + pseudo_barrier_init(g_barrier1, 5); + + // The second break synchronyizes thread exection with the breakpoint. + pseudo_barrier_init(g_barrier2, 5); + + // The third barrier keeps the waiting threads around until the breakpoint + // has been passed. + pseudo_barrier_init(g_barrier3, 4); + + // Create a thread to hit the breakpoint + pthread_create (&thread_1, NULL, break_thread_func, NULL); + + // Create more threads to slow the debugger down during processing. + pthread_create (&thread_2, NULL, wait_thread_func, NULL); + pthread_create (&thread_3, NULL, wait_thread_func, NULL); + pthread_create (&thread_4, NULL, wait_thread_func, NULL); + + // Wait for all these threads to get started. + pseudo_barrier_wait(g_barrier1); + + // Create a thread to exit during the breakpoint + pthread_create (&thread_5, NULL, exit_thread_func, NULL); + + // Wait for the threads to finish + pthread_join(thread_5, NULL); + pthread_join(thread_4, NULL); + pthread_join(thread_3, NULL); + pthread_join(thread_2, NULL); + pthread_join(thread_1, NULL); + + return 0; +} diff --git a/lldb/test/functionalities/thread/exit_during_step/Makefile b/lldb/test/functionalities/thread/exit_during_step/Makefile new file mode 100644 index 0000000..26db481 --- /dev/null +++ b/lldb/test/functionalities/thread/exit_during_step/Makefile @@ -0,0 +1,4 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/thread/exit_during_step/TestExitDuringStep.py b/lldb/test/functionalities/thread/exit_during_step/TestExitDuringStep.py new file mode 100644 index 0000000..8e6a6a69 --- /dev/null +++ b/lldb/test/functionalities/thread/exit_during_step/TestExitDuringStep.py @@ -0,0 +1,159 @@ +""" +Test number of threads. +""" + +import os, time +import unittest2 +import lldb +from lldbtest import * +import lldbutil + +class ExitDuringStepTestCase(TestBase): + + mydir = os.path.join("functionalities", "thread", "exit_during_step") + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @dsym_test + def test_with_dsym(self): + """Test thread exit during step handling.""" + self.buildDsym(dictionary=self.getBuildFlags()) + self.exit_during_step_inst_test() + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @dsym_test + def test_step_over_with_dsym(self): + """Test thread exit during step-over handling.""" + self.buildDsym(dictionary=self.getBuildFlags()) + self.exit_during_step_over_test() + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @dsym_test + def test_step_in_with_dsym(self): + """Test thread exit during step-in handling.""" + self.buildDsym(dictionary=self.getBuildFlags()) + self.exit_during_step_in_test() + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @dwarf_test + def test_with_dwarf(self): + """Test thread exit during step handling.""" + self.buildDwarf(dictionary=self.getBuildFlags()) + self.exit_during_step_inst_test() + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @dwarf_test + def test_step_over_with_dwarf(self): + """Test thread exit during step-over handling.""" + self.buildDwarf(dictionary=self.getBuildFlags()) + self.exit_during_step_over_test() + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @dwarf_test + def test_step_in_with_dwarf(self): + """Test thread exit during step-in handling.""" + self.buildDwarf(dictionary=self.getBuildFlags()) + self.exit_during_step_in_test() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers to break and continue. + self.breakpoint = line_number('main.cpp', '// Set breakpoint here') + self.continuepoint = line_number('main.cpp', '// Continue from here') + + def exit_during_step_inst_test(self): + """Test thread exit while using step-inst.""" + self.exit_during_step_base("thread step-inst -m all-threads", 'stop reason = instruction step') + + def exit_during_step_over_test(self): + """Test thread exit while using step-over.""" + self.exit_during_step_base("thread step-over -m all-threads", 'stop reason = step over') + + def exit_during_step_in_test(self): + """Test thread exit while using step-in.""" + self.exit_during_step_base("thread step-in -m all-threads", 'stop reason = step in') + + def exit_during_step_base(self, step_cmd, step_stop_reason): + """Test thread exit during step handling.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.breakpoint, num_expected_locations=1) + + # The breakpoint list should show 1 location. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file ='main.cpp', line = %d, locations = 1" % self.breakpoint]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + # Make sure we see all three threads + self.assertTrue(num_threads == 3, 'Number of expected threads and actual threads do not match.') + + # Get the thread objects + thread1 = process.GetThreadAtIndex(0) + thread2 = process.GetThreadAtIndex(1) + thread3 = process.GetThreadAtIndex(2) + + # Make sure all threads are stopped + self.assertTrue(thread1.IsStopped(), "Thread 1 didn't stop during breakpoint") + self.assertTrue(thread2.IsStopped(), "Thread 2 didn't stop during breakpoint") + self.assertTrue(thread3.IsStopped(), "Thread 3 didn't stop during breakpoint") + + # Keep stepping until we've reached our designated continue point + stepping_thread = process.GetSelectedThread() + current_line = self.breakpoint + stepping_frame = stepping_thread.GetFrameAtIndex(0) + self.assertTrue(current_line == stepping_frame.GetLineEntry().GetLine(), "Starting line for stepping doesn't match breakpoint line.") + while current_line != self.continuepoint: + self.runCmd(step_cmd) + + if stepping_thread != process.GetSelectedThread(): + process.SetSelectedThread(stepping_thread) + + frame = stepping_thread.GetFrameAtIndex(0) + + current_line = frame.GetLineEntry().GetLine() + + self.assertTrue(current_line >= self.breakpoint, "Stepped to unexpected line, " + str(current_line)) + self.assertTrue(current_line <= self.continuepoint, "Stepped to unexpected line, " + str(current_line)) + + self.runCmd("thread list") + + # Update the number of threads + num_threads = process.GetNumThreads() + + # Check to see that we reduced the number of threads as expected + self.assertTrue(num_threads == 2, 'Number of expected threads and actual threads do not match after thread exit.') + + self.expect("thread list", 'Process state is stopped due to step', + substrs = ['stopped', + step_stop_reason]) + + # Run to completion + self.runCmd("continue") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/functionalities/thread/exit_during_step/main.cpp b/lldb/test/functionalities/thread/exit_during_step/main.cpp new file mode 100644 index 0000000..9949212 --- /dev/null +++ b/lldb/test/functionalities/thread/exit_during_step/main.cpp @@ -0,0 +1,91 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test is intended to create a situation in which one thread will exit +// while a the debugger is stepping in another thread. + +#include <pthread.h> +#include <unistd.h> + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +// A barrier to synchronize thread start. +volatile int g_barrier; + +volatile int g_thread_exited = 0; + +volatile int g_test = 0; + +void * +step_thread_func (void *input) +{ + // Wait until both threads are started. + pseudo_barrier_wait(g_barrier); + + g_test = 0; // Set breakpoint here + + while (!g_thread_exited) + g_test++; + + // One more time to provide a continue point + g_test++; // Continue from here + + // Return + return NULL; +} + +void * +exit_thread_func (void *input) +{ + // Wait until both threads are started. + pseudo_barrier_wait(g_barrier); + + // Wait until the other thread is stepping. + while (g_test == 0) + do_nothing(); + + // Return + return NULL; +} + +int main () +{ + pthread_t thread_1; + pthread_t thread_2; + + // Synchronize thread start so that doesn't happen during stepping. + pseudo_barrier_init(g_barrier, 2); + + // Create a thread to hit the breakpoint. + pthread_create (&thread_1, NULL, step_thread_func, NULL); + + // Create a thread to exit while we're stepping. + pthread_create (&thread_2, NULL, exit_thread_func, NULL); + + // Wait for the exit thread to finish. + pthread_join(thread_2, NULL); + + // Let the stepping thread know the other thread is gone. + g_thread_exited = 1; + + // Wait for the stepping thread to finish. + pthread_join(thread_1, NULL); + + return 0; +} diff --git a/lldb/test/functionalities/thread/multi_break/Makefile b/lldb/test/functionalities/thread/multi_break/Makefile new file mode 100644 index 0000000..26db481 --- /dev/null +++ b/lldb/test/functionalities/thread/multi_break/Makefile @@ -0,0 +1,4 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/thread/multi_break/TestMultipleBreakpoints.py b/lldb/test/functionalities/thread/multi_break/TestMultipleBreakpoints.py new file mode 100644 index 0000000..a042284 --- /dev/null +++ b/lldb/test/functionalities/thread/multi_break/TestMultipleBreakpoints.py @@ -0,0 +1,90 @@ +""" +Test number of threads. +""" + +import os, time +import unittest2 +import lldb +from lldbtest import * +import lldbutil + +class MultipleBreakpointTestCase(TestBase): + + mydir = os.path.join("functionalities", "thread", "multi_break") + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @dsym_test + def test_with_dsym(self): + """Test simultaneous breakpoints in multiple threads.""" + self.buildDsym(dictionary=self.getBuildFlags()) + self.multiple_breakpoint_test() + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @dwarf_test + def test_with_dwarf(self): + """Test simultaneous breakpoints in multiple threads.""" + self.buildDwarf(dictionary=self.getBuildFlags()) + self.multiple_breakpoint_test() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line number for our breakpoint. + self.breakpoint = line_number('main.cpp', '// Set breakpoint here') + + def multiple_breakpoint_test(self): + """Test simultaneous breakpoints in multiple threads.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint in the main thread. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.breakpoint, num_expected_locations=1) + + # The breakpoint list should show 1 location. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file ='main.cpp', line = %d, locations = 1" % self.breakpoint]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint. + # The breakpoint may be hit in either thread 2 or thread 3. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, + substrs = ['stopped', + 'stop reason = breakpoint']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + # Make sure we see all three threads + self.assertTrue(num_threads == 3, 'Number of expected threads and actual threads do not match.') + + # Get the thread objects + thread1 = process.GetThreadAtIndex(0) + thread2 = process.GetThreadAtIndex(1) + thread3 = process.GetThreadAtIndex(2) + + # Make sure both threads are stopped + self.assertTrue(thread1.IsStopped(), "Primary thread didn't stop during breakpoint") + self.assertTrue(thread2.IsStopped(), "Secondary thread didn't stop during breakpoint") + self.assertTrue(thread3.IsStopped(), "Tertiary thread didn't stop during breakpoint") + + # Delete the first breakpoint then continue + self.runCmd("breakpoint delete 1") + + # Run to completion + self.runCmd("continue") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/functionalities/thread/multi_break/main.cpp b/lldb/test/functionalities/thread/multi_break/main.cpp new file mode 100644 index 0000000..e567e54 --- /dev/null +++ b/lldb/test/functionalities/thread/multi_break/main.cpp @@ -0,0 +1,64 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test is intended to create a situation in which a breakpoint will be +// hit in two threads at nearly the same moment. The expected result is that +// the breakpoint in the second thread will be hit while the breakpoint handler +// in the first thread is trying to stop all threads. + +#include <pthread.h> +#include <atomic> + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +std::atomic_int g_barrier; + +volatile int g_test = 0; + +void * +thread_func (void *input) +{ + // Wait until both threads are running + pseudo_barrier_wait(g_barrier); + + // Do something + g_test++; // Set breakpoint here + + // Return + return NULL; +} + +int main () +{ + pthread_t thread_1; + pthread_t thread_2; + + // Don't let either thread do anything until they're both ready. + pseudo_barrier_init(g_barrier, 2); + + // Create two threads + pthread_create (&thread_1, NULL, thread_func, NULL); + pthread_create (&thread_2, NULL, thread_func, NULL); + + // Wait for the threads to finish + pthread_join(thread_1, NULL); + pthread_join(thread_2, NULL); + + return 0; +} diff --git a/lldb/test/functionalities/thread/state/Makefile b/lldb/test/functionalities/thread/state/Makefile index 0cde994..3b5a8a3 100644 --- a/lldb/test/functionalities/thread/state/Makefile +++ b/lldb/test/functionalities/thread/state/Makefile @@ -1,5 +1,4 @@ LEVEL = ../../../make C_SOURCES := main.c -LD_EXTRAS := -lpthread include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/thread/state/TestThreadStates.py b/lldb/test/functionalities/thread/state/TestThreadStates.py index 22b51bf..fe9fb20 100644 --- a/lldb/test/functionalities/thread/state/TestThreadStates.py +++ b/lldb/test/functionalities/thread/state/TestThreadStates.py @@ -8,7 +8,7 @@ import lldb from lldbtest import * import lldbutil -class StopThreadsTestCase(TestBase): +class ThreadStateTestCase(TestBase): mydir = os.path.join("functionalities", "thread", "state") @@ -16,54 +16,54 @@ class StopThreadsTestCase(TestBase): @dsym_test def test_state_after_breakpoint_with_dsym(self): """Test thread state after breakpoint.""" - self.buildDsym() + self.buildDsym(dictionary=self.getBuildFlags(use_cpp11=False)) self.thread_state_after_breakpoint_test() @dwarf_test def test_state_after_breakpoint_with_dwarf(self): """Test thread state after breakpoint.""" - self.buildDwarf() + self.buildDwarf(dictionary=self.getBuildFlags(use_cpp11=False)) self.thread_state_after_breakpoint_test() @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") @dsym_test def test_state_after_continue_with_dsym(self): """Test thread state after continue.""" - self.buildDsym() + self.buildDsym(dictionary=self.getBuildFlags(use_cpp11=False)) self.thread_state_after_continue_test() @dwarf_test def test_state_after_continue_with_dwarf(self): """Test thread state after continue.""" - self.buildDwarf() + self.buildDwarf(dictionary=self.getBuildFlags(use_cpp11=False)) self.thread_state_after_continue_test() @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") @dsym_test def test_state_after_expression_with_dsym(self): """Test thread state after expression.""" - self.buildDsym() + self.buildDsym(dictionary=self.getBuildFlags(use_cpp11=False)) self.thread_state_after_continue_test() @dwarf_test def test_state_after_expression_with_dwarf(self): """Test thread state after expression.""" - self.buildDwarf() + self.buildDwarf(dictionary=self.getBuildFlags(use_cpp11=False)) self.thread_state_after_continue_test() @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") @dsym_test - @unittest2.expectedFailure("llvm.org/pr15824") # thread states not properly maintained + @unittest2.expectedFailure("llvm.org/pr16172") # thread states not properly maintained def test_process_interrupt_with_dsym(self): """Test process interrupt.""" - self.buildDsym() + self.buildDsym(dictionary=self.getBuildFlags(use_cpp11=False)) self.process_interrupt_test() @dwarf_test - @unittest2.expectedFailure("llvm.org/pr15824") # thread states not properly maintained + @unittest2.expectedFailure("llvm.org/pr16712") # thread states not properly maintained def test_process_interrupt_with_dwarf(self): """Test process interrupt.""" - self.buildDwarf() + self.buildDwarf(dictionary=self.getBuildFlags(use_cpp11=False)) self.process_interrupt_test() @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") @@ -71,14 +71,14 @@ class StopThreadsTestCase(TestBase): @unittest2.expectedFailure("llvm.org/pr15824") # thread states not properly maintained def test_process_state_with_dsym(self): """Test thread states (comprehensive).""" - self.buildDsym() + self.buildDsym(dictionary=self.getBuildFlags(use_cpp11=False)) self.thread_states_test() @dwarf_test @unittest2.expectedFailure("llvm.org/pr15824") # thread states not properly maintained def test_process_state_with_dwarf(self): """Test thread states (comprehensive).""" - self.buildDwarf() + self.buildDwarf(dictionary=self.getBuildFlags(use_cpp11=False)) self.thread_states_test() def setUp(self): diff --git a/lldb/test/functionalities/thread/thread_exit/Makefile b/lldb/test/functionalities/thread/thread_exit/Makefile new file mode 100644 index 0000000..26db481 --- /dev/null +++ b/lldb/test/functionalities/thread/thread_exit/Makefile @@ -0,0 +1,4 @@ +LEVEL = ../../../make + +CXX_SOURCES := main.cpp +include $(LEVEL)/Makefile.rules diff --git a/lldb/test/functionalities/thread/thread_exit/TestThreadExit.py b/lldb/test/functionalities/thread/thread_exit/TestThreadExit.py new file mode 100644 index 0000000..7fd4f91 --- /dev/null +++ b/lldb/test/functionalities/thread/thread_exit/TestThreadExit.py @@ -0,0 +1,132 @@ +""" +Test number of threads. +""" + +import os, time +import unittest2 +import lldb +from lldbtest import * +import lldbutil + +class ThreadExitTestCase(TestBase): + + mydir = os.path.join("functionalities", "thread", "thread_exit") + + @unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin") + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @dsym_test + def test_with_dsym(self): + """Test thread exit handling.""" + self.buildDsym(dictionary=self.getBuildFlags()) + self.thread_exit_test() + + @expectedFailureDarwin("llvm.org/pr15824") # thread states not properly maintained + @dwarf_test + def test_with_dwarf(self): + """Test thread exit handling.""" + self.buildDwarf(dictionary=self.getBuildFlags()) + self.thread_exit_test() + + def setUp(self): + # Call super's setUp(). + TestBase.setUp(self) + # Find the line numbers for our breakpoints. + self.break_1 = line_number('main.cpp', '// Set first breakpoint here') + self.break_2 = line_number('main.cpp', '// Set second breakpoint here') + self.break_3 = line_number('main.cpp', '// Set third breakpoint here') + self.break_4 = line_number('main.cpp', '// Set fourth breakpoint here') + + def thread_exit_test(self): + """Test thread exit handling.""" + exe = os.path.join(os.getcwd(), "a.out") + self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) + + # This should create a breakpoint with 1 location. + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_1, num_expected_locations=1) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_2, num_expected_locations=1) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_3, num_expected_locations=1) + lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.break_4, num_expected_locations=1) + + # The breakpoint list should show 1 locations. + self.expect("breakpoint list -f", "Breakpoint location shown correctly", + substrs = ["1: file ='main.cpp', line = %d, locations = 1" % self.break_1, + "2: file ='main.cpp', line = %d, locations = 1" % self.break_2, + "3: file ='main.cpp', line = %d, locations = 1" % self.break_3, + "4: file ='main.cpp', line = %d, locations = 1" % self.break_4]) + + # Run the program. + self.runCmd("run", RUN_SUCCEEDED) + + # The stop reason of the thread should be breakpoint 1. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT + " 1", + substrs = ['stopped', + '* thread #1', + 'stop reason = breakpoint 1', + 'thread #2']) + + # Get the target process + target = self.dbg.GetSelectedTarget() + process = target.GetProcess() + + # Get the number of threads + num_threads = process.GetNumThreads() + + self.assertTrue(num_threads == 2, 'Number of expected threads and actual threads do not match at breakpoint 1.') + + # Run to the second breakpoint + self.runCmd("continue") + + # The stop reason of the thread should be breakpoint 1. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT + " 2", + substrs = ['stopped', + 'thread #1', + '* thread #2', + 'stop reason = breakpoint 2', + 'thread #3']) + + # Update the number of threads + num_threads = process.GetNumThreads() + + self.assertTrue(num_threads == 3, 'Number of expected threads and actual threads do not match at breakpoint 2.') + + # Run to the third breakpoint + self.runCmd("continue") + + # The stop reason of the thread should be breakpoint 3. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT + " 3", + substrs = ['stopped', + '* thread #1', + 'stop reason = breakpoint 3', + 'thread #3', + ]) + + # Update the number of threads + num_threads = process.GetNumThreads() + + self.assertTrue(num_threads == 2, 'Number of expected threads and actual threads do not match at breakpoint 3.') + + # Run to the fourth breakpoint + self.runCmd("continue") + + # The stop reason of the thread should be breakpoint 4. + self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT + " 4", + substrs = ['stopped', + '* thread #1', + 'stop reason = breakpoint 4']) + + # Update the number of threads + num_threads = process.GetNumThreads() + + self.assertTrue(num_threads == 1, 'Number of expected threads and actual threads do not match at breakpoint 4.') + + # Run to completion + self.runCmd("continue") + + # At this point, the inferior process should have exited. + self.assertTrue(process.GetState() == lldb.eStateExited, PROCESS_EXITED) + +if __name__ == '__main__': + import atexit + lldb.SBDebugger.Initialize() + atexit.register(lambda: lldb.SBDebugger.Terminate()) + unittest2.main() diff --git a/lldb/test/functionalities/thread/thread_exit/main.cpp b/lldb/test/functionalities/thread/thread_exit/main.cpp new file mode 100644 index 0000000..94ed517 --- /dev/null +++ b/lldb/test/functionalities/thread/thread_exit/main.cpp @@ -0,0 +1,89 @@ +//===-- main.cpp ------------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +// This test verifies the correct handling of child thread exits. + +#include <pthread.h> +#include <atomic> + +// Note that although hogging the CPU while waiting for a variable to change +// would be terrible in production code, it's great for testing since it +// avoids a lot of messy context switching to get multiple threads synchronized. +#define do_nothing() + +#define pseudo_barrier_wait(bar) \ + --bar; \ + while (bar > 0) \ + do_nothing(); + +#define pseudo_barrier_init(bar, count) (bar = count) + +std::atomic_int g_barrier1; +std::atomic_int g_barrier2; +std::atomic_int g_barrier3; + +void * +thread1 (void *input) +{ + // Synchronize with the main thread. + pseudo_barrier_wait(g_barrier1); + + // Synchronize with the main thread and thread2. + pseudo_barrier_wait(g_barrier2); + + // Return + return NULL; // Set second breakpoint here +} + +void * +thread2 (void *input) +{ + // Synchronize with thread1 and the main thread. + pseudo_barrier_wait(g_barrier2); + + // Synchronize with the main thread. + pseudo_barrier_wait(g_barrier3); + + // Return + return NULL; +} + +int main () +{ + pthread_t thread_1; + pthread_t thread_2; + pthread_t thread_3; + + pseudo_barrier_init(g_barrier1, 2); + pseudo_barrier_init(g_barrier2, 3); + pseudo_barrier_init(g_barrier3, 2); + + // Create a thread. + pthread_create (&thread_1, NULL, thread1, NULL); + + // Wait for thread1 to start. + pseudo_barrier_wait(g_barrier1); + + // Create another thread. + pthread_create (&thread_2, NULL, thread2, NULL); // Set first breakpoint here + + // Wait for thread2 to start. + pseudo_barrier_wait(g_barrier2); + + // Wait for the first thread to finish + pthread_join(thread_1, NULL); + + // Synchronize with the remaining thread + pseudo_barrier_wait(g_barrier3); // Set third breakpoint here + + // Wait for the second thread to finish + pthread_join(thread_2, NULL); + + return 0; // Set fourth breakpoint here +} diff --git a/lldb/test/functionalities/watchpoint/hello_watchlocation/TestWatchLocation.py b/lldb/test/functionalities/watchpoint/hello_watchlocation/TestWatchLocation.py index b9e6a51..1e0b93b 100644 --- a/lldb/test/functionalities/watchpoint/hello_watchlocation/TestWatchLocation.py +++ b/lldb/test/functionalities/watchpoint/hello_watchlocation/TestWatchLocation.py @@ -21,7 +21,6 @@ class HelloWatchLocationTestCase(TestBase): self.setTearDownCleanup(dictionary=self.d) self.hello_watchlocation() - @expectedFailureLinux # llvm.org/pr14416 @dwarf_test def test_hello_watchlocation_with_dwarf(self): """Test watching a location with '-x size' option.""" diff --git a/lldb/test/functionalities/watchpoint/multiple_threads/TestWatchpointMultipleThreads.py b/lldb/test/functionalities/watchpoint/multiple_threads/TestWatchpointMultipleThreads.py index fabfd70..2bf5d84 100644 --- a/lldb/test/functionalities/watchpoint/multiple_threads/TestWatchpointMultipleThreads.py +++ b/lldb/test/functionalities/watchpoint/multiple_threads/TestWatchpointMultipleThreads.py @@ -21,7 +21,7 @@ class WatchpointForMultipleThreadsTestCase(TestBase): self.setTearDownCleanup(dictionary=self.d) self.hello_multiple_threads() - @skipIfLinux # llvm.org/pr14323 - skip due to incomplete multi-threaded debug support + @skipIfGcc # causes intermittent gcc debian buildbot failures, skip until we can investigate @dwarf_test def test_watchpoint_multiple_threads_with_dwarf(self): """Test that lldb watchpoint works for multiple threads.""" @@ -37,7 +37,7 @@ class WatchpointForMultipleThreadsTestCase(TestBase): self.setTearDownCleanup(dictionary=self.d) self.hello_multiple_threads_wp_set_and_then_delete() - @skipIfLinux # llvm.org/pr14323 - skip due to incomplete multi-threaded debug support + @skipIfGcc # causes intermittent gcc debian buildbot failures, skip until we can investigate @dwarf_test def test_watchpoint_multiple_threads_wp_set_and_then_delete_with_dwarf(self): """Test that lldb watchpoint works for multiple threads, and after the watchpoint is deleted, the watchpoint event should no longer fires.""" diff --git a/lldb/test/functionalities/watchpoint/watchpoint_set_command/TestWatchLocationWithWatchSet.py b/lldb/test/functionalities/watchpoint/watchpoint_set_command/TestWatchLocationWithWatchSet.py index 1db5f3b..2172c5f 100644 --- a/lldb/test/functionalities/watchpoint/watchpoint_set_command/TestWatchLocationWithWatchSet.py +++ b/lldb/test/functionalities/watchpoint/watchpoint_set_command/TestWatchLocationWithWatchSet.py @@ -20,7 +20,6 @@ class WatchLocationUsingWatchpointSetTestCase(TestBase): self.setTearDownCleanup(dictionary=self.d) self.watchlocation_using_watchpoint_set() - @expectedFailureLinux # llvm.org/pr14416 @dwarf_test def test_watchlocation_with_dwarf_using_watchpoint_set(self): """Test watching a location with 'watchpoint set expression -w write -x size' option.""" diff --git a/lldb/test/lang/c/strings/TestCStrings.py b/lldb/test/lang/c/strings/TestCStrings.py index f0a0b91..5870451 100644 --- a/lldb/test/lang/c/strings/TestCStrings.py +++ b/lldb/test/lang/c/strings/TestCStrings.py @@ -66,6 +66,9 @@ class CStringsTestCase(TestBase): self.expect("p (int)strlen(\"\")", substrs = ['(int) $', ' = 0']) + self.expect("expression !z", + substrs = ['false']) + if __name__ == '__main__': import atexit lldb.SBDebugger.Initialize() diff --git a/lldb/test/lang/c/struct_types/TestStructTypes.py b/lldb/test/lang/c/struct_types/TestStructTypes.py index 62ebf8e..e716a69 100644 --- a/lldb/test/lang/c/struct_types/TestStructTypes.py +++ b/lldb/test/lang/c/struct_types/TestStructTypes.py @@ -89,7 +89,8 @@ class StructTypesTestCase(TestBase): DATA_TYPES_DISPLAYED_CORRECTLY, substrs = ['padding[]']) # Once rdar://problem/12566646 is fixed, this should display correctly - + self.expect("expression -- &pt == (struct point_tag*)0", + substrs = ['false']) if __name__ == '__main__': import atexit diff --git a/lldb/test/lang/objc/objc-stepping/TestObjCStepping.py b/lldb/test/lang/objc/objc-stepping/TestObjCStepping.py index fb6be9e..48900d2 100644 --- a/lldb/test/lang/objc/objc-stepping/TestObjCStepping.py +++ b/lldb/test/lang/objc/objc-stepping/TestObjCStepping.py @@ -94,7 +94,7 @@ class TestObjCStepping(TestBase): self.assertTrue(mySource, "Found mySource local variable.") mySource_isa = mySource.GetChildMemberWithName ("isa") self.assertTrue(mySource_isa, "Found mySource->isa local variable.") - mySource_isa.GetValue () + className = mySource_isa.GetSummary () if self.TraceOn(): print mySource_isa @@ -135,14 +135,13 @@ class TestObjCStepping(TestBase): threads = lldbutil.continue_to_breakpoint (process, break3) self.assertTrue (len(threads) == 1, "Continued to third breakpoint in main, our object should now be swizzled.") - - mySource_isa.GetValue () - did_change = mySource_isa.GetValueDidChange () + + newClassName = mySource_isa.GetSummary () if self.TraceOn(): print mySource_isa - self.assertTrue (did_change, "The isa did indeed change, swizzled!") + self.assertTrue (newClassName != className, "The isa did indeed change, swizzled!") # Now step in, that should leave us in the Source randomMethod: thread = threads[0] diff --git a/lldb/test/lldbtest.py b/lldb/test/lldbtest.py index a7a2211..4ab7d26 100644 --- a/lldb/test/lldbtest.py +++ b/lldb/test/lldbtest.py @@ -34,6 +34,7 @@ $ import os, sys, traceback import os.path import re +import signal from subprocess import * import StringIO import time @@ -101,6 +102,8 @@ STEP_OUT_SUCCEEDED = "Thread step-out succeeded" STOPPED_DUE_TO_EXC_BAD_ACCESS = "Process should be stopped due to bad access exception" +STOPPED_DUE_TO_ASSERT = "Process should be stopped due to an assertion" + STOPPED_DUE_TO_BREAKPOINT = "Process should be stopped due to breakpoint" STOPPED_DUE_TO_BREAKPOINT_WITH_STOP_REASON_AS = "%s, %s" % ( @@ -368,7 +371,7 @@ def dwarf_test(func): wrapper.__dwarf_test__ = True return wrapper -def expectedFailureGcc(bugnumber=None): +def expectedFailureGcc(bugnumber=None, compiler_version=["=", None]): if callable(bugnumber): @wraps(bugnumber) def expectedFailureGcc_easy_wrapper(*args, **kwargs): @@ -378,7 +381,7 @@ def expectedFailureGcc(bugnumber=None): try: bugnumber(*args, **kwargs) except Exception: - if "gcc" in test_compiler: + if "gcc" in test_compiler and self.expectedCompilerVersion(compiler_version): raise case._ExpectedFailure(sys.exc_info(),None) else: raise @@ -395,7 +398,7 @@ def expectedFailureGcc(bugnumber=None): try: func(*args, **kwargs) except Exception: - if "gcc" in test_compiler: + if "gcc" in test_compiler and self.expectedCompilerVersion(compiler_version): raise case._ExpectedFailure(sys.exc_info(),bugnumber) else: raise @@ -513,7 +516,7 @@ def expectedFailurei386(bugnumber=None): return wrapper return expectedFailurei386_impl -def expectedFailureLinux(bugnumber=None): +def expectedFailureLinux(bugnumber=None, compilers=None): if callable(bugnumber): @wraps(bugnumber) def expectedFailureLinux_easy_wrapper(*args, **kwargs): @@ -523,11 +526,11 @@ def expectedFailureLinux(bugnumber=None): try: bugnumber(*args, **kwargs) except Exception: - if "linux" in platform: + if "linux" in platform and self.expectedCompiler(compilers): raise case._ExpectedFailure(sys.exc_info(),None) else: raise - if "linux" in platform: + if "linux" in platform and self.expectedCompiler(compilers): raise case._UnexpectedSuccess(sys.exc_info(),None) return expectedFailureLinux_easy_wrapper else: @@ -540,11 +543,11 @@ def expectedFailureLinux(bugnumber=None): try: func(*args, **kwargs) except Exception: - if "linux" in platform: + if "linux" in platform and self.expectedCompiler(compilers): raise case._ExpectedFailure(sys.exc_info(),bugnumber) else: raise - if "linux" in platform: + if "linux" in platform and self.expectedCompiler(compilers): raise case._UnexpectedSuccess(sys.exc_info(),bugnumber) return wrapper return expectedFailureLinux_impl @@ -818,6 +821,9 @@ class Base(unittest2.TestCase): # List of spawned subproces.Popen objects self.subprocesses = [] + # List of forked process PIDs + self.forkedProcessPids = [] + # Create a string buffer to record the session info, to be dumped into a # test case specific file if test failure is encountered. self.session = StringIO.StringIO() @@ -884,6 +890,10 @@ class Base(unittest2.TestCase): p.terminate() del p del self.subprocesses[:] + # Ensure any forked processes are cleaned up + for pid in self.forkedProcessPids: + if os.path.exists("/proc/" + str(pid)): + os.kill(pid, signal.SIGTERM) def spawnSubprocess(self, executable, args=[]): """ Creates a subprocess.Popen object with the specified executable and arguments, @@ -902,6 +912,29 @@ class Base(unittest2.TestCase): self.subprocesses.append(proc) return proc + def forkSubprocess(self, executable, args=[]): + """ Fork a subprocess with its own group ID. + NOTE: if using this function, ensure you also call: + + self.addTearDownHook(self.cleanupSubprocesses) + + otherwise the test suite will leak processes. + """ + child_pid = os.fork() + if child_pid == 0: + # If more I/O support is required, this can be beefed up. + fd = os.open(os.devnull, os.O_RDWR) + os.dup2(fd, 0) + os.dup2(fd, 1) + os.dup2(fd, 2) + # This call causes the child to have its of group ID + os.setpgid(0,0) + os.execvp(executable, [executable] + args) + # Give the child time to get through the execvp() call + time.sleep(0.1) + self.forkedProcessPids.append(child_pid) + return child_pid + def HideStdout(self): """Hide output to stdout from the user. @@ -1149,6 +1182,42 @@ class Base(unittest2.TestCase): version = m.group(1) return version + def expectedCompilerVersion(self, compiler_version): + """Returns True iff compiler_version[1] matches the current compiler version. + Use compiler_version[0] to specify the operator used to determine if a match has occurred. + Any operator other than the following defaults to an equality test: + '>', '>=', "=>", '<', '<=', '=<', '!=', "!" or 'not' + """ + if (compiler_version == None): + return True + operator = str(compiler_version[0]) + version = compiler_version[1] + + if (version == None): + return True + if (operator == '>'): + return self.getCompilerVersion() > version + if (operator == '>=' or operator == '=>'): + return self.getCompilerVersion() >= version + if (operator == '<'): + return self.getCompilerVersion() < version + if (operator == '<=' or operator == '=<'): + return self.getCompilerVersion() <= version + if (operator == '!=' or operator == '!' or operator == 'not'): + return str(version) not in str(self.getCompilerVersion()) + return str(version) in str(self.getCompilerVersion()) + + def expectedCompiler(self, compilers): + """Returns True iff any element of compilers is a sub-string of the current compiler.""" + if (compilers == None): + return True + + for compiler in compilers: + if compiler in self.getCompiler(): + return True + + return False + def getRunOptions(self): """Command line option for -A and -C to run this test again, called from self.dumpSessionInfo().""" @@ -1223,6 +1292,29 @@ class Base(unittest2.TestCase): if not module.buildDwarf(self, architecture, compiler, dictionary, clean): raise Exception("Don't know how to build binary with dwarf") + def getBuildFlags(self, use_cpp11=True, use_pthreads=True): + """ Returns a dictionary (which can be provided to build* functions above) which + contains OS-specific build flags. + """ + cflags = "" + if use_cpp11: + cflags += "-std=" + if "gcc" in self.getCompiler() and "4.6" in self.getCompilerVersion(): + cflags += "c++0x" + else: + cflags += "c++11" + if sys.platform.startswith("darwin"): + cflags += " -stdlib=libc++" + elif "clang" in self.getCompiler(): + cflags += " -stdlib=libstdc++" + + if use_pthreads: + ldflags = "-lpthread" + + return {'CFLAGS_EXTRAS' : cflags, + 'LD_EXTRAS' : ldflags, + } + def cleanup(self, dictionary=None): """Platform specific way to do cleanup after build.""" if lldb.skip_build_and_cleanup: diff --git a/lldb/test/make/Makefile.rules b/lldb/test/make/Makefile.rules index 20b172f..8c4288d 100644 --- a/lldb/test/make/Makefile.rules +++ b/lldb/test/make/Makefile.rules @@ -112,6 +112,17 @@ cxx_compiler = $(if $(findstring clang,$(1)), $(subst clang,clang++,$(1)), $(if cxx_linker = $(if $(findstring clang,$(1)), $(subst clang,clang++,$(1)), $(if $(findstring icc,$(1)), $(subst icc,icpc,$(1)), $(if $(findstring llvm-gcc,$(1)), $(subst llvm-gcc,llvm-g++,$(1)), $(subst gcc,g++,$(1))))) #---------------------------------------------------------------------- +# C++ standard library options +#---------------------------------------------------------------------- +ifeq (1,$(USE_LIBSTDCPP)) + # Clang requires an extra flag: -stdlib=libstdc++ + ifneq (,$(findstring clang,$(CC))) + CFLAGS += -stdlib=libstdc++ + LDFLAGS += -stdlib=libstdc++ + endif +endif + +#---------------------------------------------------------------------- # dylib settings #---------------------------------------------------------------------- ifneq "$(strip $(DYLIB_C_SOURCES))" "" diff --git a/lldb/test/python_api/watchpoint/watchlocation/TestSetWatchlocation.py b/lldb/test/python_api/watchpoint/watchlocation/TestSetWatchlocation.py index cfd4e58..f77e1e26a 100644 --- a/lldb/test/python_api/watchpoint/watchlocation/TestSetWatchlocation.py +++ b/lldb/test/python_api/watchpoint/watchlocation/TestSetWatchlocation.py @@ -30,7 +30,7 @@ class SetWatchlocationAPITestCase(TestBase): self.buildDsym() self.do_set_watchlocation() - @expectedFailureLinux # llvm.org/pr14416 + @skipIfLinux # Sometimes passes, sometimes not. @python_api_test @dwarf_test def test_watch_location_with_dwarf(self): diff --git a/lldb/test/python_api/watchpoint/watchlocation/TestTargetWatchAddress.py b/lldb/test/python_api/watchpoint/watchlocation/TestTargetWatchAddress.py index 303128a..ffbdd04 100644 --- a/lldb/test/python_api/watchpoint/watchlocation/TestTargetWatchAddress.py +++ b/lldb/test/python_api/watchpoint/watchlocation/TestTargetWatchAddress.py @@ -30,7 +30,7 @@ class TargetWatchAddressAPITestCase(TestBase): self.buildDsym() self.do_set_watchaddress() - @expectedFailureLinux # llvm.org/pr14416 + @skipIfLinux # llvm.org/pr14323 - skip due to incomplete multi-threaded debug support @python_api_test @dwarf_test def test_watch_address_with_dwarf(self): diff --git a/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj b/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj index 9137a76..7e029c7 100644 --- a/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj +++ b/lldb/tools/debugserver/debugserver.xcodeproj/project.pbxproj @@ -97,8 +97,6 @@ 26C637E80C71334A0024798E /* dbgnub-mig.defs */ = {isa = PBXFileReference; explicitFileType = sourcecode.mig; fileEncoding = 30; path = "dbgnub-mig.defs"; sourceTree = "<group>"; }; 26C637EA0C71334A0024798E /* DNBArchImplI386.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = DNBArchImplI386.cpp; sourceTree = "<group>"; }; 26C637EB0C71334A0024798E /* DNBArchImplI386.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = DNBArchImplI386.h; sourceTree = "<group>"; }; - 26C637EC0C71334A0024798E /* MachDYLD.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = MachDYLD.cpp; sourceTree = "<group>"; }; - 26C637ED0C71334A0024798E /* MachDYLD.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MachDYLD.h; sourceTree = "<group>"; }; 26C637EE0C71334A0024798E /* MachException.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = MachException.cpp; sourceTree = "<group>"; }; 26C637EF0C71334A0024798E /* MachException.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; path = MachException.h; sourceTree = "<group>"; }; 26C637F00C71334A0024798E /* MachProcess.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; path = MachProcess.cpp; sourceTree = "<group>"; }; @@ -277,8 +275,6 @@ 4971AE7013D10F4F00649E37 /* HasAVX.h */, 4971AE7113D10F4F00649E37 /* HasAVX.s */, 26C637E80C71334A0024798E /* dbgnub-mig.defs */, - 26C637ED0C71334A0024798E /* MachDYLD.h */, - 26C637EC0C71334A0024798E /* MachDYLD.cpp */, 26C637EF0C71334A0024798E /* MachException.h */, 26C637EE0C71334A0024798E /* MachException.cpp */, 26C637F10C71334A0024798E /* MachProcess.h */, diff --git a/lldb/tools/debugserver/source/DNB.cpp b/lldb/tools/debugserver/source/DNB.cpp index 96e3da9..b91452a 100644 --- a/lldb/tools/debugserver/source/DNB.cpp +++ b/lldb/tools/debugserver/source/DNB.cpp @@ -900,15 +900,6 @@ DNBProcessResetEvents (nub_process_t pid, nub_event_t event_mask) procSP->Events().ResetEvents(event_mask); } -void -DNBProcessInterruptEvents (nub_process_t pid) -{ - MachProcessSP procSP; - if (GetProcessSP (pid, procSP)) - procSP->Events().SetEvents(eEventProcessAsyncInterrupt); -} - - // Breakpoints nub_break_t DNBBreakpointSet (nub_process_t pid, nub_addr_t addr, nub_size_t size, nub_bool_t hardware) diff --git a/lldb/tools/debugserver/source/DNB.h b/lldb/tools/debugserver/source/DNB.h index cd5cd61..54a4620 100644 --- a/lldb/tools/debugserver/source/DNB.h +++ b/lldb/tools/debugserver/source/DNB.h @@ -104,7 +104,6 @@ nub_size_t DNBProcessGetArgumentCount (nub_process_t pid) DNB_EXPORT; //---------------------------------------------------------------------- nub_event_t DNBProcessWaitForEvents (nub_process_t pid, nub_event_t event_mask, bool wait_for_set, struct timespec* timeout) DNB_EXPORT; void DNBProcessResetEvents (nub_process_t pid, nub_event_t event_mask) DNB_EXPORT; -void DNBProcessInterruptEvents (nub_process_t pid) DNB_EXPORT; //---------------------------------------------------------------------- // Thread functions diff --git a/lldb/tools/debugserver/source/DNBBreakpoint.cpp b/lldb/tools/debugserver/source/DNBBreakpoint.cpp index 5b94693..6670687 100644 --- a/lldb/tools/debugserver/source/DNBBreakpoint.cpp +++ b/lldb/tools/debugserver/source/DNBBreakpoint.cpp @@ -288,6 +288,15 @@ DNBBreakpointList::GetByIndex (uint32_t i) return NULL; } +void +DNBBreakpointList::DisableAll () +{ + iterator pos, end = m_breakpoints.end(); + for (pos = m_breakpoints.begin(); pos != end; ++pos) + (*pos).SetEnabled(false); +} + + const DNBBreakpoint * DNBBreakpointList::GetByIndex (uint32_t i) const { diff --git a/lldb/tools/debugserver/source/DNBBreakpoint.h b/lldb/tools/debugserver/source/DNBBreakpoint.h index 4c2a112..e6a3406 100644 --- a/lldb/tools/debugserver/source/DNBBreakpoint.h +++ b/lldb/tools/debugserver/source/DNBBreakpoint.h @@ -145,6 +145,7 @@ public: size_t Size() const { return m_breakpoints.size(); } DNBBreakpoint * GetByIndex (uint32_t i); const DNBBreakpoint * GetByIndex (uint32_t i) const; + void DisableAll (); protected: typedef std::list<DNBBreakpoint> collection; diff --git a/lldb/tools/debugserver/source/DNBDefs.h b/lldb/tools/debugserver/source/DNBDefs.h index 1b8d11f..e4c95a9 100644 --- a/lldb/tools/debugserver/source/DNBDefs.h +++ b/lldb/tools/debugserver/source/DNBDefs.h @@ -121,13 +121,11 @@ enum eEventSharedLibsStateChange = 1 << 2, // Shared libraries loaded/unloaded state has changed eEventStdioAvailable = 1 << 3, // Something is available on stdout/stderr eEventProfileDataAvailable = 1 << 4, // Profile data ready for retrieval - eEventProcessAsyncInterrupt = 1 << 5, // Gives the ability for any infinite wait calls to be interrupted kAllEventsMask = eEventProcessRunningStateChanged | eEventProcessStoppedStateChanged | eEventSharedLibsStateChange | eEventStdioAvailable | - eEventProfileDataAvailable | - eEventProcessAsyncInterrupt + eEventProfileDataAvailable }; #define LOG_VERBOSE (1u << 0) @@ -239,7 +237,8 @@ enum DNBThreadStopType { eStopTypeInvalid = 0, eStopTypeSignal, - eStopTypeException + eStopTypeException, + eStopTypeExec }; enum DNBMemoryPermissions @@ -357,6 +356,7 @@ enum DNBProfileDataScanType eProfileMemory = (1 << 6), // By default, excludes eProfileMemoryDirtyPage. eProfileMemoryDirtyPage = (1 << 7), // Assume eProfileMemory, get Dirty Page size as well. + eProfileMemoryAnonymous = (1 << 8), // Assume eProfileMemory, get Anonymous memory as well. eProfileAll = 0xffffffff }; diff --git a/lldb/tools/debugserver/source/MacOSX/MachProcess.cpp b/lldb/tools/debugserver/source/MacOSX/MachProcess.cpp index 8f04a09..7397fe9 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachProcess.cpp +++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.cpp @@ -95,12 +95,14 @@ MachProcess::MachProcess() : m_state (eStateUnloaded), m_state_mutex (PTHREAD_MUTEX_RECURSIVE), m_events (0, kAllEventsMask), + m_private_events (0, kAllEventsMask), m_breakpoints (), m_watchpoints (), m_name_to_addr_callback(NULL), m_name_to_addr_baton(NULL), m_image_infos_callback(NULL), - m_image_infos_baton(NULL) + m_image_infos_baton(NULL), + m_did_exec (false) { DNBLogThreadedIf(LOG_PROCESS | LOG_VERBOSE, "%s", __PRETTY_FUNCTION__); } @@ -198,9 +200,15 @@ MachProcess::SetCurrentThread(nub_thread_t tid) } bool -MachProcess::GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const +MachProcess::GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) { - return m_thread_list.GetThreadStoppedReason(tid, stop_info); + if (m_thread_list.GetThreadStoppedReason(tid, stop_info)) + { + if (m_did_exec) + stop_info->reason = eStopTypeExec; + return true; + } + return false; } void @@ -258,8 +266,6 @@ MachProcess::SetState(nub_state_t new_state) // Scope for mutex locker { PTHREAD_MUTEX_LOCKER(locker, m_state_mutex); - DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState ( %s )", DNBStateAsString(new_state)); - const nub_state_t old_state = m_state; if (old_state != new_state) @@ -269,15 +275,26 @@ MachProcess::SetState(nub_state_t new_state) else event_mask = eEventProcessRunningStateChanged; + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState ( old = %s, new = %s ) updating state, event_mask = 0x%8.8x", DNBStateAsString(old_state), DNBStateAsString(new_state), event_mask); + m_state = new_state; if (new_state == eStateStopped) m_stop_count++; } + else + { + DNBLogThreadedIf(LOG_PROCESS, "MachProcess::SetState ( old = %s, new = %s ) ignoring state...", DNBStateAsString(old_state), DNBStateAsString(new_state)); + } } if (event_mask != 0) { m_events.SetEvents (event_mask); + m_private_events.SetEvents (event_mask); + if (event_mask == eEventProcessStoppedStateChanged) + m_private_events.ResetEvents (eEventProcessRunningStateChanged); + else + m_private_events.ResetEvents (eEventProcessStoppedStateChanged); // Wait for the event bit to reset if a reset ACK is requested m_events.WaitForResetAck(event_mask); @@ -419,7 +436,7 @@ MachProcess::Signal (int signal, const struct timespec *timeout_abstime) if (IsRunning(state) && timeout_abstime) { DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) waiting for signal to stop process...", signal, timeout_abstime); - m_events.WaitForSetEvents(eEventProcessStoppedStateChanged, timeout_abstime); + m_private_events.WaitForSetEvents(eEventProcessStoppedStateChanged, timeout_abstime); state = GetState(); DNBLogThreadedIf(LOG_PROCESS, "MachProcess::Signal (signal = %d, timeout = %p) state = %s", signal, timeout_abstime, DNBStateAsString(state)); return !IsRunning (state); @@ -823,6 +840,11 @@ MachProcess::DisableBreakpoint(nub_break_t breakID, bool remove) DNBBreakpoint *bp = m_breakpoints.FindByID (breakID); if (bp) { + // After "exec" we might end up with a bunch of breakpoints that were disabled + // manually, just ignore them + if (!bp->IsEnabled()) + return true; + nub_addr_t addr = bp->Address(); DNBLogThreadedIf(LOG_BREAKPOINTS | LOG_VERBOSE, "MachProcess::DisableBreakpoint ( breakID = %d, remove = %d ) addr = 0x%8.8llx", breakID, remove, (uint64_t)addr); @@ -1145,13 +1167,62 @@ MachProcess::ExceptionMessageBundleComplete() DNBLogThreadedIf(LOG_EXCEPTIONS, "%s: %llu exception messages.", __PRETTY_FUNCTION__, (uint64_t)m_exception_messages.size()); if (!m_exception_messages.empty()) { + m_did_exec = false; + // First check for any SIGTRAP and make sure we didn't exec + const task_t task = m_task.TaskPort(); + size_t i; + if (m_pid != 0) + { + for (i=0; i<m_exception_messages.size(); ++i) + { + if (m_exception_messages[i].state.task_port == task) + { + const int signo = m_exception_messages[i].state.SoftSignal(); + if (signo == SIGTRAP) + { + // SIGTRAP could mean that we exec'ed. We need to check the + // dyld all_image_infos.infoArray to see if it is NULL and if + // so, say that we exec'ed. + const nub_addr_t aii_addr = GetDYLDAllImageInfosAddress(); + if (aii_addr != INVALID_NUB_ADDRESS) + { + const nub_addr_t info_array_count_addr = aii_addr + 4; + uint32_t info_array_count = 0; + if (m_task.ReadMemory(info_array_count_addr, 4, &info_array_count) == 4) + { + DNBLog ("info_array_count is 0x%x", info_array_count); + if (info_array_count == 0) + m_did_exec = true; + } + else + { + DNBLog ("error: failed to read all_image_infos.infoArrayCount from 0x%8.8llx", info_array_count_addr); + } + } + break; + } + } + } + + if (m_did_exec) + { + cpu_type_t process_cpu_type = MachProcess::GetCPUTypeForLocalProcess (m_pid); + if (m_cpu_type != process_cpu_type) + { + DNBLog ("arch changed from 0x%8.8x to 0x%8.8x", m_cpu_type, process_cpu_type); + m_cpu_type = process_cpu_type; + DNBArchProtocol::SetArchitecture (process_cpu_type); + } + m_thread_list.Clear(); + m_breakpoints.DisableAll(); + } + } + // Let all threads recover from stopping and do any clean up based // on the previous thread state (if any). m_thread_list.ProcessDidStop(this); // Let each thread know of any exceptions - task_t task = m_task.TaskPort(); - size_t i; for (i=0; i<m_exception_messages.size(); ++i) { // Let the thread list figure use the MachProcess to forward all exceptions diff --git a/lldb/tools/debugserver/source/MacOSX/MachProcess.h b/lldb/tools/debugserver/source/MacOSX/MachProcess.h index eee40e4..ee2642e 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachProcess.h +++ b/lldb/tools/debugserver/source/MacOSX/MachProcess.h @@ -18,7 +18,6 @@ #include "DNBBreakpoint.h" #include "DNBError.h" #include "DNBThreadResumeActions.h" -//#include "MachDYLD.h" #include "MachException.h" #include "MachVMMemory.h" #include "MachTask.h" @@ -179,7 +178,7 @@ public: nub_thread_t GetCurrentThreadMachPort (); nub_thread_t SetCurrentThread (nub_thread_t tid); MachThreadList & GetThreadList() { return m_thread_list; } - bool GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info) const; + bool GetThreadStoppedReason(nub_thread_t tid, struct DNBThreadStopInfo *stop_info); void DumpThreadStoppedReason(nub_thread_t tid) const; const char * GetThreadInfo (nub_thread_t tid) const; @@ -300,6 +299,7 @@ private: nub_state_t m_state; // The state of our process PThreadMutex m_state_mutex; // Multithreaded protection for m_state PThreadEvent m_events; // Process related events in the child processes lifetime can be waited upon + PThreadEvent m_private_events; // Used to coordinate running and stopping the process without affecting m_events DNBBreakpointList m_breakpoints; // Breakpoint list for this process DNBBreakpointList m_watchpoints; // Watchpoint list for this process DNBCallbackNameToAddress m_name_to_addr_callback; @@ -307,6 +307,7 @@ private: DNBCallbackCopyExecutableImageInfos m_image_infos_callback; void * m_image_infos_baton; + bool m_did_exec; }; diff --git a/lldb/tools/debugserver/source/MacOSX/MachTask.cpp b/lldb/tools/debugserver/source/MacOSX/MachTask.cpp index f80d479..30fc22c 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachTask.cpp +++ b/lldb/tools/debugserver/source/MacOSX/MachTask.cpp @@ -364,7 +364,9 @@ MachTask::GetProfileData (DNBProfileDataScanType scanType) mach_vm_size_t vprvt = 0; mach_vm_size_t vsize = 0; mach_vm_size_t dirty_size = 0; - if (m_vm_memory.GetMemoryProfile(scanType, task, task_info, m_process->GetCPUType(), m_process->ProcessID(), vm_stats, physical_memory, rprvt, rsize, vprvt, vsize, dirty_size)) + mach_vm_size_t purgeable = 0; + mach_vm_size_t anonymous = 0; + if (m_vm_memory.GetMemoryProfile(scanType, task, task_info, m_process->GetCPUType(), m_process->ProcessID(), vm_stats, physical_memory, rprvt, rsize, vprvt, vsize, dirty_size, purgeable, anonymous)) { std::ostringstream profile_data_stream; @@ -439,6 +441,12 @@ MachTask::GetProfileData (DNBProfileDataScanType scanType) if (scanType & eProfileMemoryDirtyPage) profile_data_stream << "dirty:" << dirty_size << ';'; + + if (scanType & eProfileMemoryAnonymous) + { + profile_data_stream << "purgeable:" << purgeable << ';'; + profile_data_stream << "anonymous:" << anonymous << ';'; + } } profile_data_stream << "--end--;"; diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp index 17879c3..af82fc9 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp +++ b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.cpp @@ -17,6 +17,7 @@ #include <mach/mach_vm.h> #include <mach/shared_region.h> #include <sys/sysctl.h> +#include <dlfcn.h> MachVMMemory::MachVMMemory() : m_page_size (kInvalidPageSize), @@ -281,14 +282,7 @@ MachVMMemory::GetRegionSizes(task_t task, mach_vm_size_t &rsize, mach_vm_size_t } } - static vm_size_t pagesize; - static bool calculated = false; - if (!calculated) - { - calculated = true; - pagesize = PageSize (task); - } - + vm_size_t pagesize = PageSize (task); rsize = pages_resident * pagesize; dirty_size = pages_dirtied * pagesize; } @@ -333,14 +327,7 @@ MachVMMemory::GetMemorySizes(task_t task, cpu_type_t cputype, nub_process_t pid, mach_vm_size_t aliased = 0; bool global_shared_text_data_mapped = false; - - static vm_size_t pagesize; - static bool calculated = false; - if (!calculated) - { - calculated = true; - pagesize = PageSize (task); - } + vm_size_t pagesize = PageSize (task); for (mach_vm_address_t addr=0, size=0; ; addr += size) { @@ -430,8 +417,72 @@ MachVMMemory::GetMemorySizes(task_t task, cpu_type_t cputype, nub_process_t pid, rprvt += aliased; } +#if defined (TASK_VM_INFO) && TASK_VM_INFO >= 22 + +// cribbed from sysmond +static uint64_t +SumVMPurgeableInfo(const vm_purgeable_info_t info) +{ + uint64_t sum = 0; + int i; + + for (i = 0; i < 8; i++) + { + sum += info->fifo_data[i].size; + } + sum += info->obsolete_data.size; + for (i = 0; i < 8; i++) + { + sum += info->lifo_data[i].size; + } + + return sum; +} + +#endif + +static void +GetPurgeableAndAnonymous(task_t task, uint64_t &purgeable, uint64_t &anonymous) +{ +#if defined (TASK_VM_INFO) && TASK_VM_INFO >= 22 + + kern_return_t kr; + task_purgable_info_t purgeable_info; + uint64_t purgeable_sum = 0; + mach_msg_type_number_t info_count; + task_vm_info_data_t vm_info; + + typedef kern_return_t (*task_purgable_info_type) (task_t, task_purgable_info_t *); + task_purgable_info_type task_purgable_info_ptr = NULL; + task_purgable_info_ptr = (task_purgable_info_type)dlsym(RTLD_NEXT, "task_purgable_info"); + if (task_purgable_info_ptr != NULL) + { + kr = (*task_purgable_info_ptr)(task, &purgeable_info); + if (kr == KERN_SUCCESS) { + purgeable_sum = SumVMPurgeableInfo(&purgeable_info); + purgeable = purgeable_sum; + } + } + + info_count = TASK_VM_INFO_COUNT; + kr = task_info(task, TASK_VM_INFO, (task_info_t)&vm_info, &info_count); + if (kr == KERN_SUCCESS) + { + if (purgeable_sum < vm_info.internal) + { + anonymous = vm_info.internal - purgeable_sum; + } + else + { + anonymous = 0; + } + } + +#endif +} + nub_bool_t -MachVMMemory::GetMemoryProfile(DNBProfileDataScanType scanType, task_t task, struct task_basic_info ti, cpu_type_t cputype, nub_process_t pid, vm_statistics_data_t &vm_stats, uint64_t &physical_memory, mach_vm_size_t &rprvt, mach_vm_size_t &rsize, mach_vm_size_t &vprvt, mach_vm_size_t &vsize, mach_vm_size_t &dirty_size) +MachVMMemory::GetMemoryProfile(DNBProfileDataScanType scanType, task_t task, struct task_basic_info ti, cpu_type_t cputype, nub_process_t pid, vm_statistics_data_t &vm_stats, uint64_t &physical_memory, mach_vm_size_t &rprvt, mach_vm_size_t &rsize, mach_vm_size_t &vprvt, mach_vm_size_t &vsize, mach_vm_size_t &dirty_size, mach_vm_size_t &purgeable, mach_vm_size_t &anonymous) { if (scanType & eProfileHostMemory) physical_memory = GetPhysicalMemory(); @@ -453,6 +504,11 @@ MachVMMemory::GetMemoryProfile(DNBProfileDataScanType scanType, task_t task, str // This uses vmmap strategy. We don't use the returned rsize for now. We prefer to match top's version since that's what we do for the rest of the metrics. GetRegionSizes(task, rsize, dirty_size); } + + if (scanType & eProfileMemoryAnonymous) + { + GetPurgeableAndAnonymous(task, purgeable, anonymous); + } } return true; diff --git a/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h index 3c544a0..565ead0 100644 --- a/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h +++ b/lldb/tools/debugserver/source/MacOSX/MachVMMemory.h @@ -28,7 +28,7 @@ public: nub_size_t Write(task_t task, nub_addr_t address, const void *data, nub_size_t data_count); nub_size_t PageSize(task_t task); nub_bool_t GetMemoryRegionInfo(task_t task, nub_addr_t address, DNBRegionInfo *region_info); - nub_bool_t GetMemoryProfile(DNBProfileDataScanType scanType, task_t task, struct task_basic_info ti, cpu_type_t cputype, nub_process_t pid, vm_statistics_data_t &vm_stats, uint64_t &physical_memory, mach_vm_size_t &rprvt, mach_vm_size_t &rsize, mach_vm_size_t &vprvt, mach_vm_size_t &vsize, mach_vm_size_t &dirty_size); + nub_bool_t GetMemoryProfile(DNBProfileDataScanType scanType, task_t task, struct task_basic_info ti, cpu_type_t cputype, nub_process_t pid, vm_statistics_data_t &vm_stats, uint64_t &physical_memory, mach_vm_size_t &rprvt, mach_vm_size_t &rsize, mach_vm_size_t &vprvt, mach_vm_size_t &vsize, mach_vm_size_t &dirty_size, mach_vm_size_t &purgable, mach_vm_size_t &anonymous); protected: nub_size_t MaxBytesLeftInPage(task_t task, nub_addr_t addr, nub_size_t count); diff --git a/lldb/tools/debugserver/source/RNBRemote.cpp b/lldb/tools/debugserver/source/RNBRemote.cpp index e530bff..d711a97 100644 --- a/lldb/tools/debugserver/source/RNBRemote.cpp +++ b/lldb/tools/debugserver/source/RNBRemote.cpp @@ -1079,7 +1079,7 @@ RNBRemote::Initialize() bool -RNBRemote::InitializeRegisters () +RNBRemote::InitializeRegisters (bool force) { pid_t pid = m_ctx.ProcessID(); if (pid == INVALID_NUB_PROCESS) @@ -1093,6 +1093,13 @@ RNBRemote::InitializeRegisters () // registers to be discovered using multiple qRegisterInfo calls to get // all register information after the architecture for the process is // determined. + if (force) + { + g_dynamic_register_map.clear(); + g_reg_entries = NULL; + g_num_reg_entries = 0; + } + if (g_dynamic_register_map.empty()) { nub_size_t num_reg_sets = 0; @@ -2381,6 +2388,10 @@ RNBRemote::SendStopReplyPacketForThread (nub_thread_t tid) if (DNBThreadGetStopReason (pid, tid, &tid_stop_info)) { + const bool did_exec = tid_stop_info.reason == eStopTypeExec; + if (did_exec) + RNBRemote::InitializeRegisters(true); + std::ostringstream ostrm; // Output the T packet with the thread ostrm << 'T'; @@ -2478,8 +2489,12 @@ RNBRemote::SendStopReplyPacketForThread (nub_thread_t tid) } } } - - if (tid_stop_info.details.exception.type) + + if (did_exec) + { + ostrm << "reason:exec;"; + } + else if (tid_stop_info.details.exception.type) { ostrm << "metype:" << std::hex << tid_stop_info.details.exception.type << ";"; ostrm << "mecount:" << std::hex << tid_stop_info.details.exception.data_count << ";"; diff --git a/lldb/tools/debugserver/source/RNBRemote.h b/lldb/tools/debugserver/source/RNBRemote.h index ab688a3..b8b3b25 100644 --- a/lldb/tools/debugserver/source/RNBRemote.h +++ b/lldb/tools/debugserver/source/RNBRemote.h @@ -129,7 +129,7 @@ public: void Initialize(); - bool InitializeRegisters (); + bool InitializeRegisters (bool force = false); rnb_err_t HandleAsyncPacket(PacketEnum *type = NULL); rnb_err_t HandleReceivedPacket(PacketEnum *type = NULL); diff --git a/lldb/tools/driver/CMakeLists.txt b/lldb/tools/driver/CMakeLists.txt index 8aaf5f0..c70a2db 100644 --- a/lldb/tools/driver/CMakeLists.txt +++ b/lldb/tools/driver/CMakeLists.txt @@ -12,5 +12,7 @@ target_link_libraries(lldb liblldb) #target_link_libraries(lldb ${LLDB_USED_LIBS})
#llvm_config(lldb ${LLVM_LINK_COMPONENTS})
+set_target_properties(lldb PROPERTIES VERSION ${LLDB_VERSION})
+
install(TARGETS lldb
RUNTIME DESTINATION bin)
diff --git a/lldb/tools/driver/Driver.cpp b/lldb/tools/driver/Driver.cpp index bd46801..875adc2 100644 --- a/lldb/tools/driver/Driver.cpp +++ b/lldb/tools/driver/Driver.cpp @@ -105,7 +105,9 @@ static OptionDefinition g_options[] = "Tells the debugger to open source files using the host's \"external editor\" mechanism." }, { LLDB_3_TO_5, false, "no-lldbinit" , 'x', no_argument , 0, eArgTypeNone, "Do not automatically parse any '.lldbinit' files." }, - { LLDB_OPT_SET_6, true , "python-path" , 'P', no_argument , 0, eArgTypeNone, + { LLDB_3_TO_5, false, "no-use-colors" , 'o', no_argument , 0, eArgTypeNone, + "Do not use colors." }, + { LLDB_OPT_SET_6, true , "python-path" , 'P', no_argument , 0, eArgTypeNone, "Prints out the path to the lldb.py file for this version of lldb." }, { 0, false, NULL , 0 , 0 , 0, eArgTypeNone, NULL } }; @@ -621,6 +623,10 @@ Driver::ParseArgs (int argc, const char *argv[], FILE *out_fh, bool &exit) m_debugger.SkipAppInitFiles (true); break; + case 'o': + m_debugger.SetUseColor (false); + break; + case 'f': { SBFileSpec file(optarg); @@ -1587,9 +1593,15 @@ Driver::MainLoop () } } - editline_output_pty.CloseMasterFileDescriptor(); + master_out_comm.SetReadThreadBytesReceivedCallback(NULL, NULL); master_out_comm.Disconnect(); + master_out_comm.ReadThreadStop(); + + out_comm_2.SetReadThreadBytesReceivedCallback(NULL, NULL); out_comm_2.Disconnect(); + out_comm_2.ReadThreadStop(); + + editline_output_pty.CloseMasterFileDescriptor(); reset_stdin_termios(); fclose (stdin); diff --git a/lldb/utils/vim-lldb/doc/lldb.txt b/lldb/utils/vim-lldb/doc/lldb.txt index dae9651..e54e6f2 100644 --- a/lldb/utils/vim-lldb/doc/lldb.txt +++ b/lldb/utils/vim-lldb/doc/lldb.txt @@ -37,6 +37,12 @@ Possible window name arguments to the Lhide and Lshow commands include: * locals * registers * threads + *lldb-:Lattach* +:Lattach <process-name> Attach to a process by name. + + *lldb-:Ldetach* +:Ldetach Detach from the current process. + *lldb-:Ltarget* :Ltarget [[create] executable] Create a target with the specified executable. If @@ -84,6 +90,13 @@ Possible window name arguments to the Lhide and Lshow commands include: command is invoked. If no arguments are provided, a breakpoint at the location under the cursor. + *lldb-:Lprint* + *lldb-:Lpo* + *lldb-:LpO* +:Lprint <expr> Aliases to the lldb print and po commands. Cursor +:Lpo <expr> word (cursor WORD for LpO) will be used when +:LpO <expr> expression omitted. + MAPPINGS *lldb-mappings* On Mac OS X (under MacVim) , the following key mappings are available: diff --git a/lldb/utils/vim-lldb/plugin/lldb.vim b/lldb/utils/vim-lldb/plugin/lldb.vim index c69fe7b..ac5cfe3 100644 --- a/lldb/utils/vim-lldb/plugin/lldb.vim +++ b/lldb/utils/vim-lldb/plugin/lldb.vim @@ -42,11 +42,13 @@ function! s:InitLldbPlugin() " Window show/hide commands command -complete=custom,s:CompleteWindow -nargs=1 Lhide python ctrl.doHide('<args>') - command -complete=custom,s:CompleteWindow -nargs=1 Lshow python ctrl.doShow('<args>') + command -complete=custom,s:CompleteWindow -nargs=0 Lshow python ctrl.doShow('<args>') " Launching convenience commands (no autocompletion) command -nargs=* Lstart python ctrl.doLaunch(True, '<args>') command -nargs=* Lrun python ctrl.doLaunch(False, '<args>') + command -nargs=1 Lattach python ctrl.doAttach('<args>') + command -nargs=0 Ldetach python ctrl.doDetach() " Regexp-commands: because vim's command mode does not support '_' or '-' " characters in command names, we omit them when creating the :L<cmd> @@ -80,7 +82,9 @@ function! s:InitLldbPlugin() command -complete=custom,s:CompleteCommand -nargs=* Lwatchpoint python ctrl.doCommand('watchpoint', '<args>') " Convenience (shortcut) LLDB commands - command -complete=custom,s:CompleteCommand -nargs=* Lprint python ctrl.doCommand('print', '<args>') + command -complete=custom,s:CompleteCommand -nargs=* Lprint python ctrl.doCommand('print', vim.eval("s:CursorWord('<args>')")) + command -complete=custom,s:CompleteCommand -nargs=* Lpo python ctrl.doCommand('po', vim.eval("s:CursorWord('<args>')")) + command -complete=custom,s:CompleteCommand -nargs=* LpO python ctrl.doCommand('po', vim.eval("s:CursorWORD('<args>')")) command -complete=custom,s:CompleteCommand -nargs=* Lbt python ctrl.doCommand('bt', '<args>') " Frame/Thread-Selection (commands that also do an Uupdate but do not @@ -133,4 +137,15 @@ returnCompleteWindow(a, l, p) EOF endfunction() +" Returns cword if search term is empty +function! s:CursorWord(term) + return empty(a:term) ? expand('<cword>') : a:term +endfunction() + +" Returns cleaned cWORD if search term is empty +function! s:CursorWORD(term) + " Will strip all non-alphabetic characters from both sides + return empty(a:term) ? substitute(expand('<cWORD>'), '^\A*\(.\{-}\)\A*$', '\1', '') : a:term +endfunction() + call s:InitLldbPlugin() diff --git a/lldb/utils/vim-lldb/python-vim-lldb/lldb_controller.py b/lldb/utils/vim-lldb/python-vim-lldb/lldb_controller.py index 77f5563..923e771 100644 --- a/lldb/utils/vim-lldb/python-vim-lldb/lldb_controller.py +++ b/lldb/utils/vim-lldb/python-vim-lldb/lldb_controller.py @@ -141,6 +141,29 @@ class LLDBController(object): else: self.doLaunch('-s' not in args, "") + def doAttach(self, process_name): + """ Handle process attach. """ + error = lldb.SBError() + + self.processListener = lldb.SBListener("process_event_listener") + self.target = self.dbg.CreateTarget('') + self.process = self.target.AttachToProcessWithName(self.processListener, process_name, False, error) + if not error.Success(): + sys.stderr.write("Error during attach: " + str(error)) + return + + self.ui.activate() + self.pid = self.process.GetProcessID() + + print "Attached to %s (pid=%d)" % (process_name, self.pid) + + def doDetach(self): + if self.process is not None and self.process.IsValid(): + pid = self.process.GetProcessID() + state = state_type_to_str(self.process.GetState()) + self.process.Detach() + self.processPendingEvents(self.eventDelayLaunch) + def doLaunch(self, stop_at_entry, args): """ Handle process launch. """ error = lldb.SBError() @@ -250,6 +273,10 @@ class LLDBController(object): def doShow(self, name): """ handle :Lshow <name> """ + if not name: + self.ui.activate() + return + if self.ui.showWindow(name): self.ui.update(self.target, "", self) @@ -321,6 +348,10 @@ class LLDBController(object): self.processListener.GetNextEvent(event) new_state = lldb.SBProcess.GetStateFromEvent(event) + # continue if stopped after attaching + if old_state == lldb.eStateAttaching and new_state == lldb.eStateStopped: + self.process.Continue() + # If needed, perform any event-specific behaviour here num_events_handled += 1 diff --git a/lldb/www/architecture.html b/lldb/www/architecture.html index 98920c4..98b91b2 100755 --- a/lldb/www/architecture.html +++ b/lldb/www/architecture.html @@ -60,7 +60,7 @@ <li>Classes can't inherit from any other classes.</li>
<li>Classes can't contain virtual methods.</li>
<li>Classes should be compatible with script bridging utilities like <a href="http://www.swig.org/">swig</a>.</li>
- <li>Classes should be lightweight and be backed by a single object pointer, shared pointer or global variable in the lldb_private.</li>
+ <li>Classes should be lightweight and be backed by a single member. Pointers (or shared pointers) are the preferred choice since they allow changing the contents of the backend without affecting the public object layout.</li>
<li>The interface should be as minimal as possible in order to give a complete API.</li>
</ul>
<p>By adhering to these rules we should be able to continue to
diff --git a/lldb/www/build.html b/lldb/www/build.html index c07a823..89b413b 100755 --- a/lldb/www/build.html +++ b/lldb/www/build.html @@ -81,6 +81,17 @@ <code>> yum install swig python-devel libedit-devel</code>
<p>On an Ubuntu system one might run:</p>
<code>> sudo apt-get install build-essential subversion swig python-dev libedit-dev </code>
+ <p>If you wish to build the optional reference documentation, additional dependencies are required:</p>
+ <ul>
+ <li> Graphviz (for the 'dot' tool).
+ <li> doxygen (only if you wish to build the C++ API reference)
+ <li> epydoc (only if you wish to build the Python API reference)
+ </ul>
+ <p>To install the prerequisites for building the documentation (on Ubuntu) do:</p>
+ <code>
+ <br>> sudo apt-get install doxygen graphviz
+ <br>> sudo pip install epydoc
+ </code>
<h2 >Building LLDB</h2>
<p>We first need to checkout the source trees into the appropriate locations. Both
Clang and LLDB build as subprojects of LLVM. This means we will be checking out
@@ -131,14 +142,14 @@ ninja on your system. To build using ninja:
</p>
<code>
- <br>> cmake -C .. -G Ninja
+ > cmake -C .. -G Ninja
<br>> ninja lldb
<br>> ninja check-lldb
</code>
<h3>Using CMake + Unix Makefiles</h3>
<p>If you do not have Ninja, you can still use CMake to generate Unix Makefiles that build LLDB:</p>
<code>
- <br>> cmake -C ..
+ > cmake -C ..
<br>> make
<br>> make check-lldb
</code>
@@ -146,12 +157,21 @@ <p>If you do not have CMake, it is still possible to build LLDB using the autoconf build system. If you are using
Clang or GCC 4.7+, run:</p>
<code>
- <br>> $llvm/configure --enable-cxx11
+ > $llvm/configure --enable-cxx11
<br>> make </code>
<p>Or, if you are using a version of GCC that does not support the <tt>-std=c++11</tt> option:</p>
<code>
- <br>> $llvm/configure
+ > $llvm/configure
<br>> make CXXFLAGS=-std=c++0x</code>
+ <p>If you are building with a GCC that isn't the default gcc/g++, like gcc-4.7/g++-4.7</p>
+ <code>
+ > $llvm/configure --enable-cxx11 CC=gcc-4.7 CXX=g++-4.7
+ <br>> make CC=gcc-4.7 CXX=g++-4.7</code>
+ <p>If you are running in a system that doesn't have a lot of RAM (less than 4GB), you might want to disable
+ debug symbols by specifying DEBUG_SYMBOLS=0 when running make. You will know if you need to enable this
+ because you will fail to link clang (the linker will get a SIGKILL and exit with status 9).</p>
+ <code>
+ > make DEBUG_SYMBOLS=0</code>
<p> To run the LLDB test suite, run:</p>
<code>
<br>> make -C tools/lldb/test</code>
@@ -162,6 +182,37 @@ <tt>--enable-libcpp</tt> flag.</p>
<p> If you wish to build a release version of LLDB, run configure with the <tt>--enable-optimized</tt> flag.</p>
+ <h2>Testing</h2>
+ <p>By default, the <tt>check-lldb</tt> target builds the 64-bit variants of the test programs with the same
+ compiler that was used to build LLDB. It is possible to customize the architecture and compiler by appending -A and
+ -C options respectively to the CMake variable <tt>LLDB_TEST_ARGS</tt>. For example, to test LLDB against 32-bit binaries
+ built with a custom version of clang, do:</p>
+ <code>
+ <br>> cmake -DLLDB_TEST_ARGS="-A i386 -C /path/to/custom/clang" -G Ninja
+ <br>> ninja check-lldb
+ </code>
+ <p>Note that multiple -A and -C flags can be specified to <tt>LLDB_TEST_ARGS</tt>.</p>
+ <p>In addition to running all the LLDB test suites with the "check-lldb" CMake target above, it is possible to
+ run individual LLDB tests. For example, to run the test cases defined in TestInferiorCrashing.py, run:</p>
+ <code>
+ <br>> cd $lldb/test
+ <br>> python dotest.py --executable <path-to-lldb> -p TestInferiorCrashing.py
+ </code>
+ <p>In addition to running a test by name, it is also possible to specify a directory path to <tt>dotest.py</tt>
+ in order to run all the tests under that directory. For example, to run all the tests under the
+ 'functionalities/data-formatter' directory, run:</p>
+ <code>
+ <br>> python dotest.py --executable <path-to-lldb> functionalities/data-formatter
+ </code>
+ <p>To dump additional information to <tt>stdout</tt> about how the test harness is driving LLDB, run
+ <tt>dotest.py</tt> with the <tt>-t</tt> flag. Many more options that are available. To see a list of all of them, run:</p>
+ <code>
+ <br>> python dotest.py -h
+ </code>
+ <h2>Building API reference documentation</h2>
+ <p>LLDB exposes a C++ as well as a Python API. To build the reference documentation for these two APIs, ensure you have
+ the required dependencies installed, and build the <tt>lldb-python-doc</tt> and <tt>lldb-cpp-doc</tt> CMake targets.</p>
+ <p> The output HTML reference documentation can be found in <tt><build-dir>/tools/lldb/docs/</tt>.<p>
<h2>Additional Notes</h2>
<p>LLDB has a Python scripting capability and supplies its own Python module named <tt>lldb</tt>.
If a script is run inside the command line <tt>lldb</tt> application, the Python module
diff --git a/lldb/www/python-reference.html b/lldb/www/python-reference.html index 2a9c529..8b65d4a 100755 --- a/lldb/www/python-reference.html +++ b/lldb/www/python-reference.html @@ -271,6 +271,10 @@ frame #0: 0x0000000100000bb6 a.out main + 54 at main.c:16 </td>
</tr>
</table>
+ <p>Optionally, a Python breakpoint command can return a value. Returning False tells LLDB that you do not want to stop at the breakpoint.
+ Any other return value (including None or leaving out the return statement altogether) is akin to telling LLDB to actually stop at the breakpoint.
+ This can be useful in situations where a breakpoint only needs to stop the process when certain conditions are met, and you do not want to inspect the
+ program state manually at every stop and then continue.
<p>An example will show how simple it is to write some python code and attach it to a breakpoint.
The following example will allow you to track the order in which the functions in a given shared library
are first executed during one run of your program. This is a simple method to gather an order file which
@@ -301,8 +305,8 @@ Enter your Python command(s). Type 'DONE' to end. > <strong>print '[%i] %s' % (counter, name)</strong>
> <font color=green># Disable the current breakpoint location so it doesn't get hit again</font>
> <strong>bp_loc.SetEnabled(False)</strong>
-> <font color=green># How continue the process</font>
-> <strong>frame.GetThread().GetProcess().Continue()</strong>
+> <font color=green># No need to stop here</font>
+> <strong>return False</strong>
> <strong>DONE</strong>
</tt></pre></code>
<p>The <b>breakpoint command add</b> command above attaches a python script to breakpoint 1.
@@ -387,7 +391,8 @@ Enter your Python command(s). Type 'DONE' to end. </tr>
</table>
<p>As a convenience, you can treat the result object as a Python file object, and say
- print >>result, "my command does lots of cool stuff". SBCommandReturnObject and SBStream
+ <code><pre><tt>print >>result, "my command does lots of cool stuff"</tt></pre></code>
+ SBCommandReturnObject and SBStream
both support this file-like behavior by providing write() and flush() calls at the Python layer.</p>
<p>One other handy convenience when defining lldb command-line commands is the command
<b>command script import</b> which will import a module specified by file path - so you
diff --git a/lldb/www/status.html b/lldb/www/status.html index db2cd99..90679ff 100755 --- a/lldb/www/status.html +++ b/lldb/www/status.html @@ -34,9 +34,10 @@ <h1 class ="postheader">Linux Status</h1> <div class="postcontent"> <p> LLDB is improving on Linux. While the debugserver is not ported - (to enable remote debugging) and there are some stability issues, most - of the basic functionality, including the Python API and the commandline tool, - are working on i386/x86_64 architectures. ARM architectures on Linux are untested. + (to enable remote debugging) and debugging of multi-threaded programs is in its infancy, most + of the functionality, including the Python API and the command line tool, + is working on the x86_64 architecture, and partially working with i386. + FreeBSD is untested. ARM architectures on Linux are untested. For more details, see the Features by OS section below. </div> <h1 class ="postheader">Features by OS</h1> @@ -47,12 +48,12 @@ <table border="1"> <tr> <th>Feature</th> - <th>Linux<br>(i386 and x86_64)</th> + <th>Linux<br>(x86_64)</th> <th>Mac OS X (i386/x86_64 and ARM/Thumb)</th> </tr> <tr> <td>Backtracing</td> - <td>OK</td> + <td>OK (except with targets built with -fomit-frame-pointer)</td> <td>OK</td> </tr> <tr> @@ -119,9 +120,10 @@ <li>launch <li>attach <li>continue + <li>fork </ul> </td> - <td>OK except attach-by-name </td> + <td>OK</td> <td>OK</td> </tr> <tr> @@ -130,6 +132,19 @@ <td>OK</td> </tr> <tr> + <td>Registers (x86_64 and i386) + <ul> + <li>general purpose + <li>floating point + <li>exception state + <li>SSE + <li>AVX + </ul> + </td> + <td>OK (except for exception state registers)</td> + <td>OK</td> + </tr> + <tr> <td>Script bridging</td> <td>OK</td> <td>OK</td> diff --git a/lldb/www/varformats.html b/lldb/www/varformats.html index b5f8c8e..5e12eb8 100755 --- a/lldb/www/varformats.html +++ b/lldb/www/varformats.html @@ -32,8 +32,7 @@ different style to the display for certain datatypes. To do so, you need to give hints to the debugger as to how variables should be displayed.<br> - A new <b>type</b> command has been introduced in LLDB - which allows to do just that.<br> + The LLDB <b>type</b> command allows you to do just that.<br> </p> <p>Using it you can change your visualization to look like this: </p> @@ -50,7 +49,7 @@ style="font-style: italic;">synthetic children</span>.</p> <p>To reflect this, the <b>type</b> command has four - subcommands:<br> + subcommands (plus one specific for <i>categories</i>):<br> </p> <p><code>type format</code></p> @@ -456,9 +455,7 @@ <code>"<b>${*var.int_pointer[0-3]}</b>".</code></p> <p>Basically, the syntax is the same one described <a href="formats.html">Frame and Thread Formatting</a> - are accepted. - Beyond what's described there, additional symbols have become available - in the syntax for summary strings. The main of them is <code>${var</code>, + plus additional symbols specific for summary strings. The main of them is <code>${var</code>, which is used refer to the variable that a summary is being created for.</p> <p>The simplest thing you can do is grab a member variable of a class or structure by typing its <i>expression @@ -527,7 +524,7 @@ as in <code>${var.x->x%u}</code>, which would display the value of <code>x</code> as an unsigned integer. <p>You can also use some other special format markers, not available - for type formatters, but which carry a special meaning when used in this + for formats themselves, but which carry a special meaning when used in this context:</p> <table border="1"> @@ -872,18 +869,16 @@ def function (valobj,internal_dict):<br/> <table class="stats" width="620" cellspacing="0"> <td class="content"> <b>(lldb)</b> type summary add --python-script "height = - int(valobj.GetChildMemberWithName('height').GetValue());width = - int(valobj.GetChildMemberWithName('width').GetValue()); - return 'Area: ' + str(height*width)" Rectangle<br/> + valobj.GetChildMemberWithName('height').GetValueAsUnsigned(0);width = + valobj.GetChildMemberWithName('width').GetValueAsUnsigned(0); + return 'Area: %d' % (height*width)" Rectangle<br/> </td> </table> <ul> <li> using the <code>--python-function</code> (<code>-F</code>) option to <code>type summary add </code> and giving the name of a Python function with the correct prototype. Most probably, you will define (or have already defined) the function in the interactive interpreter, or somehow - loaded it from a file, using the <code>script import</code> command. LLDB will not make any attempt at determining whether - the function is defined and syntactically correct, until you try to call it. Any errors will be shown at that stage, as if - you were executing your function inside the Python interactive interpreter itself. + loaded it from a file, using the <code>command script import</code> command. LLDB will emit a warning if it is unable to find the function you passed, but will still register the binding. </ul> </p> @@ -914,8 +909,9 @@ def function (valobj,internal_dict):<br/> <table> <code> - <b>(lldb)</b> frame variable sarray<br> + <b>(lldb)</b> frame variable<br> (Simple [3]) sarray = [1,4,7]<br> + (Simple [2]) sother = [3,6]<br> </code> The above scenario works for <code>Simple [3]</code> as well as for any other array of <code>Simple</code> objects. </p> @@ -925,20 +921,10 @@ def function (valobj,internal_dict):<br/> matching is slower than normal name matching, LLDB will first try to match by name in any way it can, and only when this fails, will it resort to regular expression - matching. Thus, if your type has a base class with a - cascading summary, this will be preferred over any - regular expression match for your type itself.</p> + matching. </p> <p>One of the ways LLDB uses this feature internally, is to match the names of STL container classes, regardless of the template - arguments provided (e.g. <code>std::vector<T></code> for any - type argument <code>T</code>). The regular expressions used for this are: - </p> - <ul> - <li><code>^(std::)?vector<.+>$</code> for <code>std::vector<T></code></li> - <li><code>^(std::)?list<.+>$</code> for <code>std::list<T></code></li> - <li><code>^(std::)?map<.+> >$</code> for <code>std::map<K,V></code></li> - </ul> - As you can see, the actual template arguments are ignored by the regular expression. + arguments provided. The details for this are found at <a href="http://llvm.org/svn/llvm-project/lldb/trunk/source/DataFormatters/FormatManager.cpp">FormatManager.cpp</a></p> <p>The regular expression language used by LLDB is the <a href="http://en.wikipedia.org/wiki/Regular_expression#POSIX_Extended_Regular_Expressions">POSIX extended language</a>, as defined by the <a href="http://pubs.opengroup.org/onlinepubs/7908799/xsh/regex.h.html">Single UNIX Specification</a>, of which Mac OS X is a compliant implementation. @@ -1041,10 +1027,11 @@ def function (valobj,internal_dict):<br/> <font color=blue>def</font> has_children(self): <br/> <i>this call should return True if this object might have children, and False if this object can be guaranteed not to have children.</i><sup>[2]</sup><br/> </code> -<sup>[1]</sup> This method is optional. Also, it may optionally choose to return a value (starting with LLDB SVN rev153061/LLDB-134). If it returns a value, and that value is <font color=blue><code>True</code></font>, LLDB will be allowed to cache the children and the children count it previously obtained, and will not return to the provider class to ask. If nothing, <font color=blue><code>None</code></font>, or anything other than <font color=blue><code>True</code></font> is returned, LLDB will discard the cached information and ask. Regardless, whenever necessary LLDB will call <code>update</code>. +<sup>[1]</sup> This method is optional. Also, it may optionally choose to return a value (starting with SVN rev153061/LLDB-134). If it returns a value, and that value is <font color=blue><code>True</code></font>, LLDB will be allowed to cache the children and the children count it previously obtained, and will not return to the provider class to ask. If nothing, <font color=blue><code>None</code></font>, or anything other than <font color=blue><code>True</code></font> is returned, LLDB will discard the cached information and ask. Regardless, whenever necessary LLDB will call <code>update</code>. <br/> -<sup>[2]</sup> This method is optional, and LLDB will honor it starting with SVN rev166495. While implementing it in terms of <code>num_children</code> is acceptable, implementors are encouraged to look for optimized coding alternatives whenever reasonable. For an example, see the <code>std::list</code> providers shipping with LLDB. - <p>For examples of how synthetic children are created, you are encouraged to look at <a href="http://llvm.org/svn/llvm-project/lldb/trunk/examples/synthetic/">examples/synthetic</a> in the LLDB trunk. +<sup>[2]</sup> This method is optional (starting with SVN rev166495/LLDB-175). While implementing it in terms of <code>num_children</code> is acceptable, implementors are encouraged to look for optimized coding alternatives whenever reasonable. + <p>For examples of how synthetic children are created, you are encouraged to look at <a href="http://llvm.org/svn/llvm-project/lldb/trunk/examples/synthetic/">examples/synthetic</a> in the LLDB trunk. Please, be aware that the code in those files (except bitfield/) + is legacy code and is not maintained. You may especially want to begin looking at <a href="http://llvm.org/svn/llvm-project/lldb/trunk/examples/synthetic/bitfield">this example</a> to get a feel for this feature, as it is a very easy and well commented example.</p> The design pattern consistently used in synthetic providers shipping with LLDB @@ -1075,7 +1062,7 @@ def function (valobj,internal_dict):<br/> } <br/> </code> </p> - <p>LLDB has synthetic children providers for basic STL classes, both in the version provided by <a href="http://gcc.gnu.org/libstdc++/">libstdcpp</a> and by <a href="http://libcxx.llvm.org/">libcxx</a>. and for basic Cocoa containers (NSArray and NSDictionary).</p> + <p>LLDB has synthetic children providers for a core subset of STL classes, both in the version provided by <a href="http://gcc.gnu.org/libstdc++/">libstdcpp</a> and by <a href="http://libcxx.llvm.org/">libcxx</a>, as well as for several Foundation classes.</p> <p>Synthetic children extend summary strings by enabling a new special variable: <code>${svar</code>.<br/> This symbol tells LLDB to refer expression paths to the @@ -1108,7 +1095,7 @@ def function (valobj,internal_dict):<br/> error: Couldn't convert the expression to DWARF<br/> </code> </p> The reason for this is that classes might have an overloaded <code><font color="blue">operator</font> []</code>, or other special provisions - and the <code>expression</code> command ignores synthetic children when evaluating its arguments. + and the <code>expression</code> command chooses to ignore synthetic children in the interest of equivalency with code you asked to have compiled from source. </div> </div> @@ -1144,12 +1131,10 @@ def function (valobj,internal_dict):<br/> <div class="postcontent"> <p>When doing Objective-C development, you may notice that some of your variables come out as of type <code>id</code> (for instance, items extracted from <code>NSArray</code>). - While this does not influence the ability of the runtime to send messages to them, it could make it impossible for LLDB - to determine the actual formatters for that object, given its type-based algorithm.</p> - <p>The debugger, however, can dynamically discover the type of an Objective-C +By default, LLDB will not show you the real type of the object. it can actually dynamically discover the type of an Objective-C variable, much like the runtime itself does when invoking a selector. In order - to let LLDB do that, however, a special option to <code>frame variable</code> is - required: <code>--dynamic-type</code>.</p> + to be shown the result of that discovery that, however, a special option to <code>frame variable</code> or <code>expression</code> is + required: <br/><code>--dynamic-type</code>.</p> <p><code>--dynamic-type</code> can have one of three values: <ul> <li><code>no-dynamic-values</code>: the default, prevents dynamic type discovery</li> @@ -1166,18 +1151,23 @@ def function (valobj,internal_dict):<br/> </p> <p><table class="stats" width="620" cellspacing="0"> <td class="content"> - <b>(lldb)</b> frame variable ns_string --dynamic-type no-run-target --show-types + <b>(lldb)</b> expr @"Hello" + </td> + </table> + <code>(NSString *) $0 = 0x00000001048000b0 @"Hello"<br/> + </code> + <p><table class="stats" width="620" cellspacing="0"> + <td class="content"> + <b>(lldb)</b> expr -d no-run @"Hello" </td> </table> - <code>(__NSCFString *) ns_string = 0x00000001001183d0 @"An NSString saying hello world"<br/> + <code>(__NSCFString *) $1 = 0x00000001048000b0 @"Hello"<br/> </code> <p> Because LLDB uses a detection algorithm that does not need to invoke any functions - on the target process, <code>no-run-target</code> is enough for this to work. - As a final sidenote on this, LLDB is currently able to provide a summary string for <code>NSString</code> - that shows the content of the string, without requiring you to run code on the target - process. This features requires you to enable the AppKit category (see below for details). - The first implementation of this feature was a Python script (still available for reference at <a href="http://llvm.org/svn/llvm-project/lldb/trunk/examples/summaries/cocoa/CFString.py">CFString.py</a>). + on the target process, <code>no-run-target</code> is enough for this to work.</p> + As a side note, the summary for NSString shown in the example is built right into LLDB. + It was initially implemented through Python (the code is still available for reference at <a href="http://llvm.org/svn/llvm-project/lldb/trunk/examples/summaries/cocoa/CFString.py">CFString.py</a>). However, this is out of sync with the current implementation of the NSString formatter (which is a C++ function compiled into the LLDB core). </p> </div> @@ -1187,7 +1177,7 @@ def function (valobj,internal_dict):<br/> <h1 class="postheader">Categories</h1> <div class="postcontent"> <p>Categories are a way to group related formatters. For instance, LLDB itself groups - the formatters for the C++ STL objects in a category named <code>gnu-libstdc++</code>. + the formatters for the libstdc++ types in a category named <code>gnu-libstdc++</code>. Basically, categories act like containers in which to store formatters for a same library or OS release.</p> <p>By default, several categories are created in LLDB: |