From 59555d427607248018553572d84ec88dea235fbc Mon Sep 17 00:00:00 2001 From: zhuoli Date: Thu, 5 Aug 2021 20:34:27 +0800 Subject: [PATCH] Initialize lldb-mi with ohos adaptation. f6c45ba0 Signed-off-by: zhuoli --- CMakeLists.txt | 41 + LICENSE.TXT | 276 ++++ OAT.xml | 87 + README.OpenSource | 11 + README.en.md | 36 - README.md | 44 +- src/CMakeLists.txt | 100 ++ src/MICmdArgContext.cpp | 221 +++ src/MICmdArgContext.h | 43 + src/MICmdArgSet.cpp | 386 +++++ src/MICmdArgSet.h | 107 ++ src/MICmdArgValBase.cpp | 129 ++ src/MICmdArgValBase.h | 115 ++ src/MICmdArgValConsume.cpp | 88 + src/MICmdArgValConsume.h | 53 + src/MICmdArgValFile.cpp | 178 ++ src/MICmdArgValFile.h | 47 + src/MICmdArgValListBase.cpp | 209 +++ src/MICmdArgValListBase.h | 101 ++ src/MICmdArgValListOfN.cpp | 167 ++ src/MICmdArgValListOfN.h | 92 ++ src/MICmdArgValNumber.cpp | 156 ++ src/MICmdArgValNumber.h | 69 + src/MICmdArgValOptionLong.cpp | 291 ++++ src/MICmdArgValOptionLong.h | 104 ++ src/MICmdArgValOptionShort.cpp | 121 ++ src/MICmdArgValOptionShort.h | 59 + src/MICmdArgValPrintValues.cpp | 125 ++ src/MICmdArgValPrintValues.h | 56 + src/MICmdArgValString.cpp | 380 +++++ src/MICmdArgValString.h | 82 + src/MICmdArgValThreadGrp.cpp | 141 ++ src/MICmdArgValThreadGrp.h | 53 + src/MICmdBase.cpp | 329 ++++ src/MICmdBase.h | 193 +++ src/MICmdCmd.cpp | 158 ++ src/MICmdCmd.h | 90 + src/MICmdCmdBreak.cpp | 1024 ++++++++++++ src/MICmdCmdBreak.h | 262 +++ src/MICmdCmdData.cpp | 1673 +++++++++++++++++++ src/MICmdCmdData.h | 381 +++++ src/MICmdCmdEnviro.cpp | 145 ++ src/MICmdCmdEnviro.h | 57 + src/MICmdCmdExec.cpp | 1115 +++++++++++++ src/MICmdCmdExec.h | 316 ++++ src/MICmdCmdFile.cpp | 206 +++ src/MICmdCmdFile.h | 66 + src/MICmdCmdGdbInfo.cpp | 225 +++ src/MICmdCmdGdbInfo.h | 87 + src/MICmdCmdGdbSet.cpp | 491 ++++++ src/MICmdCmdGdbSet.h | 101 ++ src/MICmdCmdGdbShow.cpp | 395 +++++ src/MICmdCmdGdbShow.h | 101 ++ src/MICmdCmdGdbThread.cpp | 89 + src/MICmdCmdGdbThread.h | 50 + src/MICmdCmdMiscellanous.cpp | 586 +++++++ src/MICmdCmdMiscellanous.h | 156 ++ src/MICmdCmdStack.cpp | 1053 ++++++++++++ src/MICmdCmdStack.h | 256 +++ src/MICmdCmdSupportInfo.cpp | 117 ++ src/MICmdCmdSupportInfo.h | 58 + src/MICmdCmdSupportList.cpp | 96 ++ src/MICmdCmdSupportList.h | 51 + src/MICmdCmdSymbol.cpp | 177 ++ src/MICmdCmdSymbol.h | 59 + src/MICmdCmdTarget.cpp | 447 +++++ src/MICmdCmdTarget.h | 117 ++ src/MICmdCmdThread.cpp | 211 +++ src/MICmdCmdThread.h | 69 + src/MICmdCmdTrace.cpp | 88 + src/MICmdCmdTrace.h | 50 + src/MICmdCmdVar.cpp | 1460 ++++++++++++++++ src/MICmdCmdVar.h | 348 ++++ src/MICmdCommands.cpp | 134 ++ src/MICmdCommands.h | 19 + src/MICmdData.cpp | 10 + src/MICmdData.h | 58 + src/MICmdFactory.cpp | 206 +++ src/MICmdFactory.h | 84 + src/MICmdInterpreter.cpp | 290 ++++ src/MICmdInterpreter.h | 62 + src/MICmdInvoker.cpp | 321 ++++ src/MICmdInvoker.h | 103 ++ src/MICmdMgr.cpp | 248 +++ src/MICmdMgr.h | 69 + src/MICmdMgrSetCmdDeleteCallback.cpp | 87 + src/MICmdMgrSetCmdDeleteCallback.h | 72 + src/MICmnBase.cpp | 123 ++ src/MICmnBase.h | 46 + src/MICmnConfig.h | 19 + src/MICmnLLDBBroadcaster.cpp | 68 + src/MICmnLLDBBroadcaster.h | 44 + src/MICmnLLDBDebugSessionInfo.cpp | 863 ++++++++++ src/MICmnLLDBDebugSessionInfo.h | 292 ++++ src/MICmnLLDBDebugSessionInfoVarObj.cpp | 573 +++++++ src/MICmnLLDBDebugSessionInfoVarObj.h | 139 ++ src/MICmnLLDBDebugger.cpp | 908 ++++++++++ src/MICmnLLDBDebugger.h | 134 ++ src/MICmnLLDBDebuggerHandleEvents.cpp | 2017 +++++++++++++++++++++++ src/MICmnLLDBDebuggerHandleEvents.h | 104 ++ src/MICmnLLDBProxySBValue.cpp | 134 ++ src/MICmnLLDBProxySBValue.h | 34 + src/MICmnLLDBUtilSBValue.cpp | 497 ++++++ src/MICmnLLDBUtilSBValue.h | 77 + src/MICmnLog.cpp | 330 ++++ src/MICmnLog.h | 138 ++ src/MICmnLogMediumFile.cpp | 387 +++++ src/MICmnLogMediumFile.h | 84 + src/MICmnMIOutOfBandRecord.cpp | 202 +++ src/MICmnMIOutOfBandRecord.h | 93 ++ src/MICmnMIResultRecord.cpp | 133 ++ src/MICmnMIResultRecord.h | 79 + src/MICmnMIValue.cpp | 43 + src/MICmnMIValue.h | 50 + src/MICmnMIValueConst.cpp | 77 + src/MICmnMIValueConst.h | 59 + src/MICmnMIValueList.cpp | 177 ++ src/MICmnMIValueList.h | 55 + src/MICmnMIValueResult.cpp | 116 ++ src/MICmnMIValueResult.h | 66 + src/MICmnMIValueTuple.cpp | 194 +++ src/MICmnMIValueTuple.h | 63 + src/MICmnResources.cpp | 619 +++++++ src/MICmnResources.h | 339 ++++ src/MICmnStreamStderr.cpp | 249 +++ src/MICmnStreamStderr.h | 61 + src/MICmnStreamStdin.cpp | 220 +++ src/MICmnStreamStdin.h | 60 + src/MICmnStreamStdout.cpp | 230 +++ src/MICmnStreamStdout.h | 62 + src/MICmnThreadMgrStd.cpp | 145 ++ src/MICmnThreadMgrStd.h | 123 ++ src/MIDataTypes.h | 65 + src/MIDriver.cpp | 1318 +++++++++++++++ src/MIDriver.h | 180 ++ src/MIDriverBase.cpp | 179 ++ src/MIDriverBase.h | 67 + src/MIDriverMain.cpp | 194 +++ src/MIDriverMgr.cpp | 727 ++++++++ src/MIDriverMgr.h | 128 ++ src/MIExtensions.txt | 104 ++ src/MIReadMe.txt | 37 + src/MIUtilDateTimeStd.cpp | 84 + src/MIUtilDateTimeStd.h | 40 + src/MIUtilDebug.cpp | 91 + src/MIUtilDebug.h | 79 + src/MIUtilFileStd.cpp | 288 ++++ src/MIUtilFileStd.h | 48 + src/MIUtilMapIdToVariant.cpp | 99 ++ src/MIUtilMapIdToVariant.h | 129 ++ src/MIUtilSingletonBase.h | 52 + src/MIUtilSingletonHelper.h | 81 + src/MIUtilString.cpp | 915 ++++++++++ src/MIUtilString.h | 95 ++ src/MIUtilThreadBaseStd.cpp | 302 ++++ src/MIUtilThreadBaseStd.h | 140 ++ src/MIUtilVariant.cpp | 344 ++++ src/MIUtilVariant.h | 247 +++ src/Platform.h | 91 + src/lldb-Info.plist | 21 + src/lldb-mi.exports | 0 src/module.modulemap | 79 + 162 files changed, 35563 insertions(+), 71 deletions(-) create mode 100644 CMakeLists.txt create mode 100644 LICENSE.TXT create mode 100755 OAT.xml create mode 100755 README.OpenSource delete mode 100644 README.en.md create mode 100644 src/CMakeLists.txt create mode 100644 src/MICmdArgContext.cpp create mode 100644 src/MICmdArgContext.h create mode 100644 src/MICmdArgSet.cpp create mode 100644 src/MICmdArgSet.h create mode 100644 src/MICmdArgValBase.cpp create mode 100644 src/MICmdArgValBase.h create mode 100644 src/MICmdArgValConsume.cpp create mode 100644 src/MICmdArgValConsume.h create mode 100644 src/MICmdArgValFile.cpp create mode 100644 src/MICmdArgValFile.h create mode 100644 src/MICmdArgValListBase.cpp create mode 100644 src/MICmdArgValListBase.h create mode 100644 src/MICmdArgValListOfN.cpp create mode 100644 src/MICmdArgValListOfN.h create mode 100644 src/MICmdArgValNumber.cpp create mode 100644 src/MICmdArgValNumber.h create mode 100644 src/MICmdArgValOptionLong.cpp create mode 100644 src/MICmdArgValOptionLong.h create mode 100644 src/MICmdArgValOptionShort.cpp create mode 100644 src/MICmdArgValOptionShort.h create mode 100644 src/MICmdArgValPrintValues.cpp create mode 100644 src/MICmdArgValPrintValues.h create mode 100644 src/MICmdArgValString.cpp create mode 100644 src/MICmdArgValString.h create mode 100644 src/MICmdArgValThreadGrp.cpp create mode 100644 src/MICmdArgValThreadGrp.h create mode 100644 src/MICmdBase.cpp create mode 100644 src/MICmdBase.h create mode 100644 src/MICmdCmd.cpp create mode 100644 src/MICmdCmd.h create mode 100644 src/MICmdCmdBreak.cpp create mode 100644 src/MICmdCmdBreak.h create mode 100644 src/MICmdCmdData.cpp create mode 100644 src/MICmdCmdData.h create mode 100644 src/MICmdCmdEnviro.cpp create mode 100644 src/MICmdCmdEnviro.h create mode 100644 src/MICmdCmdExec.cpp create mode 100644 src/MICmdCmdExec.h create mode 100644 src/MICmdCmdFile.cpp create mode 100644 src/MICmdCmdFile.h create mode 100644 src/MICmdCmdGdbInfo.cpp create mode 100644 src/MICmdCmdGdbInfo.h create mode 100644 src/MICmdCmdGdbSet.cpp create mode 100644 src/MICmdCmdGdbSet.h create mode 100644 src/MICmdCmdGdbShow.cpp create mode 100644 src/MICmdCmdGdbShow.h create mode 100644 src/MICmdCmdGdbThread.cpp create mode 100644 src/MICmdCmdGdbThread.h create mode 100644 src/MICmdCmdMiscellanous.cpp create mode 100644 src/MICmdCmdMiscellanous.h create mode 100644 src/MICmdCmdStack.cpp create mode 100644 src/MICmdCmdStack.h create mode 100644 src/MICmdCmdSupportInfo.cpp create mode 100644 src/MICmdCmdSupportInfo.h create mode 100644 src/MICmdCmdSupportList.cpp create mode 100644 src/MICmdCmdSupportList.h create mode 100644 src/MICmdCmdSymbol.cpp create mode 100644 src/MICmdCmdSymbol.h create mode 100644 src/MICmdCmdTarget.cpp create mode 100644 src/MICmdCmdTarget.h create mode 100644 src/MICmdCmdThread.cpp create mode 100644 src/MICmdCmdThread.h create mode 100644 src/MICmdCmdTrace.cpp create mode 100644 src/MICmdCmdTrace.h create mode 100644 src/MICmdCmdVar.cpp create mode 100644 src/MICmdCmdVar.h create mode 100644 src/MICmdCommands.cpp create mode 100644 src/MICmdCommands.h create mode 100644 src/MICmdData.cpp create mode 100644 src/MICmdData.h create mode 100644 src/MICmdFactory.cpp create mode 100644 src/MICmdFactory.h create mode 100644 src/MICmdInterpreter.cpp create mode 100644 src/MICmdInterpreter.h create mode 100644 src/MICmdInvoker.cpp create mode 100644 src/MICmdInvoker.h create mode 100644 src/MICmdMgr.cpp create mode 100644 src/MICmdMgr.h create mode 100644 src/MICmdMgrSetCmdDeleteCallback.cpp create mode 100644 src/MICmdMgrSetCmdDeleteCallback.h create mode 100644 src/MICmnBase.cpp create mode 100644 src/MICmnBase.h create mode 100644 src/MICmnConfig.h create mode 100644 src/MICmnLLDBBroadcaster.cpp create mode 100644 src/MICmnLLDBBroadcaster.h create mode 100644 src/MICmnLLDBDebugSessionInfo.cpp create mode 100644 src/MICmnLLDBDebugSessionInfo.h create mode 100644 src/MICmnLLDBDebugSessionInfoVarObj.cpp create mode 100644 src/MICmnLLDBDebugSessionInfoVarObj.h create mode 100644 src/MICmnLLDBDebugger.cpp create mode 100644 src/MICmnLLDBDebugger.h create mode 100644 src/MICmnLLDBDebuggerHandleEvents.cpp create mode 100644 src/MICmnLLDBDebuggerHandleEvents.h create mode 100644 src/MICmnLLDBProxySBValue.cpp create mode 100644 src/MICmnLLDBProxySBValue.h create mode 100644 src/MICmnLLDBUtilSBValue.cpp create mode 100644 src/MICmnLLDBUtilSBValue.h create mode 100644 src/MICmnLog.cpp create mode 100644 src/MICmnLog.h create mode 100644 src/MICmnLogMediumFile.cpp create mode 100644 src/MICmnLogMediumFile.h create mode 100644 src/MICmnMIOutOfBandRecord.cpp create mode 100644 src/MICmnMIOutOfBandRecord.h create mode 100644 src/MICmnMIResultRecord.cpp create mode 100644 src/MICmnMIResultRecord.h create mode 100644 src/MICmnMIValue.cpp create mode 100644 src/MICmnMIValue.h create mode 100644 src/MICmnMIValueConst.cpp create mode 100644 src/MICmnMIValueConst.h create mode 100644 src/MICmnMIValueList.cpp create mode 100644 src/MICmnMIValueList.h create mode 100644 src/MICmnMIValueResult.cpp create mode 100644 src/MICmnMIValueResult.h create mode 100644 src/MICmnMIValueTuple.cpp create mode 100644 src/MICmnMIValueTuple.h create mode 100644 src/MICmnResources.cpp create mode 100644 src/MICmnResources.h create mode 100644 src/MICmnStreamStderr.cpp create mode 100644 src/MICmnStreamStderr.h create mode 100644 src/MICmnStreamStdin.cpp create mode 100644 src/MICmnStreamStdin.h create mode 100644 src/MICmnStreamStdout.cpp create mode 100644 src/MICmnStreamStdout.h create mode 100644 src/MICmnThreadMgrStd.cpp create mode 100644 src/MICmnThreadMgrStd.h create mode 100644 src/MIDataTypes.h create mode 100644 src/MIDriver.cpp create mode 100644 src/MIDriver.h create mode 100644 src/MIDriverBase.cpp create mode 100644 src/MIDriverBase.h create mode 100644 src/MIDriverMain.cpp create mode 100644 src/MIDriverMgr.cpp create mode 100644 src/MIDriverMgr.h create mode 100644 src/MIExtensions.txt create mode 100644 src/MIReadMe.txt create mode 100644 src/MIUtilDateTimeStd.cpp create mode 100644 src/MIUtilDateTimeStd.h create mode 100644 src/MIUtilDebug.cpp create mode 100644 src/MIUtilDebug.h create mode 100644 src/MIUtilFileStd.cpp create mode 100644 src/MIUtilFileStd.h create mode 100644 src/MIUtilMapIdToVariant.cpp create mode 100644 src/MIUtilMapIdToVariant.h create mode 100644 src/MIUtilSingletonBase.h create mode 100644 src/MIUtilSingletonHelper.h create mode 100644 src/MIUtilString.cpp create mode 100644 src/MIUtilString.h create mode 100644 src/MIUtilThreadBaseStd.cpp create mode 100644 src/MIUtilThreadBaseStd.h create mode 100644 src/MIUtilVariant.cpp create mode 100644 src/MIUtilVariant.h create mode 100644 src/Platform.h create mode 100644 src/lldb-Info.plist create mode 100644 src/lldb-mi.exports create mode 100644 src/module.modulemap diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 00000000000..f7e5cb1a411 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,41 @@ +cmake_minimum_required(VERSION 3.4.3) + +if(POLICY CMP0077) + cmake_policy(SET CMP0077 NEW) +endif() + +project(lldb-mi) + + +find_package(LLVM REQUIRED CONFIG) +message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") +message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") + +list(APPEND CMAKE_MODULE_PATH ${LLVM_DIR}) +include(HandleLLVMStdlib) +include(HandleLLVMOptions) + +include_directories(${LLVM_INCLUDE_DIRS}) +if(LLVM_BUILD_MAIN_SRC_DIR) + include_directories(${LLVM_BUILD_MAIN_SRC_DIR}/../lldb/include) + include_directories(${LLVM_BUILD_BINARY_DIR}/tools/lldb/include) +endif() + +add_definitions(${LLVM_DEFINITIONS}) + +if (NOT LLVM_ENABLE_EH) + if(LLVM_COMPILER_IS_GCC_COMPATIBLE) + add_compile_options("-fno-exceptions") + elseif(MSVC) + add_compile_options("/EHs-c-") + add_definitions("-D_HAS_EXCEPTIONS=0") + endif() +endif() + +if(MSVC) + #Disable warning "multiple copy constructors specified". + add_compile_options("/wd4521") +endif() + +add_subdirectory(src) + diff --git a/LICENSE.TXT b/LICENSE.TXT new file mode 100644 index 00000000000..029b1d9aae1 --- /dev/null +++ b/LICENSE.TXT @@ -0,0 +1,276 @@ +============================================================================== +The LLVM Project is under the Apache License v2.0 with LLVM Exceptions: +============================================================================== + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +---- LLVM Exceptions to the Apache 2.0 License ---- + +As an exception, if, as a result of your compiling your source code, portions +of this Software are embedded into an Object form of such source code, you +may redistribute such embedded portions in such Object form without complying +with the conditions of Sections 4(a), 4(b) and 4(d) of the License. + +In addition, if you combine or link compiled forms of this Software with +software that is licensed under the GPLv2 ("Combined Software") and if a +court of competent jurisdiction determines that the patent provision (Section +3), the indemnity provision (Section 9) or other Section of the License +conflicts with the conditions of the GPLv2, you may retroactively and +prospectively choose to deem waived or otherwise exclude such Section(s) of +the License, but only in their entirety and only with respect to the Combined +Software. + +============================================================================== +Software from third parties included in the LLVM Project: +============================================================================== +The LLVM Project contains third party software which is under different license +terms. All such code will be identified clearly using at least one of two +mechanisms: +1) It will be in a separate directory tree with its own `LICENSE.txt` or + `LICENSE` file at the top containing the specific license and restrictions + which apply to that software, or +2) It will contain specific license and restriction terms at the top of every + file. + +============================================================================== +Legacy LLVM License (https://llvm.org/docs/DeveloperPolicy.html#legacy): +============================================================================== +University of Illinois/NCSA +Open Source License + +Copyright (c) 2010 Apple Inc. +All rights reserved. + +Developed by: + + LLDB Team + + http://lldb.llvm.org/ + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal with +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimers. + + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimers in the + documentation and/or other materials provided with the distribution. + + * Neither the names of the LLDB Team, copyright holders, nor the names of + its contributors may be used to endorse or promote products derived from + this Software without specific prior written permission. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE +SOFTWARE. + diff --git a/OAT.xml b/OAT.xml new file mode 100755 index 00000000000..2f23a4e35dc --- /dev/null +++ b/OAT.xml @@ -0,0 +1,87 @@ + + + + + + LICENSE.TXT + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/README.OpenSource b/README.OpenSource new file mode 100755 index 00000000000..0c55b652848 --- /dev/null +++ b/README.OpenSource @@ -0,0 +1,11 @@ +[ + { + "Name": "LLVM", + "License": "Apache License v2.0 with LLVM Exceptions", + "License File": "LICENSE.TXT", + "Version Number": "8.0.1", + "Owner": "sunqiang13@huawei.com", + "Upstream URL": "http://llvm.org/", + "Description": "The LLVM Project is a collection of modular and reusable compiler and toolchain technologies. Despite its name, LLVM has little to do with traditional virtual machines. The name \"LLVM\" itself is not an acronym; it is the full name of the project." + } +] diff --git a/README.en.md b/README.en.md deleted file mode 100644 index 7f451c3f0ce..00000000000 --- a/README.en.md +++ /dev/null @@ -1,36 +0,0 @@ -# thid_party_lldb_mi - -#### Description -{**When you're done, you can delete the content in this README and update the file with details for others getting started with your repository**} - -#### Software Architecture -Software architecture description - -#### Installation - -1. xxxx -2. xxxx -3. xxxx - -#### Instructions - -1. xxxx -2. xxxx -3. xxxx - -#### Contribution - -1. Fork the repository -2. Create Feat_xxx branch -3. Commit your code -4. Create Pull Request - - -#### Gitee Feature - -1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md -2. Gitee blog [blog.gitee.com](https://blog.gitee.com) -3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore) -4. The most valuable open source project [GVP](https://gitee.com/gvp) -5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help) -6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) diff --git a/README.md b/README.md index 7158723f0b8..e7990ee2a7f 100644 --- a/README.md +++ b/README.md @@ -1,39 +1,13 @@ -# thid_party_lldb_mi +# lldb-mi -#### 介绍 -{**以下是 Gitee 平台说明,您可以替换此简介** -Gitee 是 OSCHINA 推出的基于 Git 的代码托管平台(同时支持 SVN)。专为开发者提供稳定、高效、安全的云端软件开发协作平台 -无论是个人、团队、或是企业,都能够用 Gitee 实现代码托管、项目管理、协作开发。企业项目请看 [https://gitee.com/enterprises](https://gitee.com/enterprises)} +This is the LLDB's machine interface driver from llvm-8.0.1. +A few patches and OHOS target modifications are applied. -#### 软件架构 -软件架构说明 +# Build +The build of lldb-mi relies on LLDB, Clang and LLVM. On a system that's already configured with LLDB, CLANG and LLVM, +the lldb-mi can be built with the following commond: -#### 安装教程 - -1. xxxx -2. xxxx -3. xxxx - -#### 使用说明 - -1. xxxx -2. xxxx -3. xxxx - -#### 参与贡献 - -1. Fork 本仓库 -2. 新建 Feat_xxx 分支 -3. 提交代码 -4. 新建 Pull Request - - -#### 特技 - -1. 使用 Readme\_XXX.md 来支持不同的语言,例如 Readme\_en.md, Readme\_zh.md -2. Gitee 官方博客 [blog.gitee.com](https://blog.gitee.com) -3. 你可以 [https://gitee.com/explore](https://gitee.com/explore) 这个地址来了解 Gitee 上的优秀开源项目 -4. [GVP](https://gitee.com/gvp) 全称是 Gitee 最有价值开源项目,是综合评定出的优秀开源项目 -5. Gitee 官方提供的使用手册 [https://gitee.com/help](https://gitee.com/help) -6. Gitee 封面人物是一档用来展示 Gitee 会员风采的栏目 [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/) +```bash +cmake . ; cmake --build . +``` diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 00000000000..90821016b5b --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,100 @@ +add_executable(lldb-mi + MICmdArgContext.cpp + MICmdArgSet.cpp + MICmdArgValBase.cpp + MICmdArgValConsume.cpp + MICmdArgValFile.cpp + MICmdArgValListBase.cpp + MICmdArgValListOfN.cpp + MICmdArgValNumber.cpp + MICmdArgValOptionLong.cpp + MICmdArgValOptionShort.cpp + MICmdArgValPrintValues.cpp + MICmdArgValString.cpp + MICmdArgValThreadGrp.cpp + MICmdBase.cpp + MICmdCommands.cpp + MICmdCmd.cpp + MICmdCmdBreak.cpp + MICmdCmdData.cpp + MICmdCmdEnviro.cpp + MICmdCmdExec.cpp + MICmdCmdFile.cpp + MICmdCmdGdbInfo.cpp + MICmdCmdGdbSet.cpp + MICmdCmdGdbShow.cpp + MICmdCmdGdbThread.cpp + MICmdCmdMiscellanous.cpp + MICmdCmdStack.cpp + MICmdCmdSupportInfo.cpp + MICmdCmdSupportList.cpp + MICmdCmdSymbol.cpp + MICmdCmdTarget.cpp + MICmdCmdThread.cpp + MICmdCmdTrace.cpp + MICmdCmdVar.cpp + MICmdData.cpp + MICmdFactory.cpp + MICmdInterpreter.cpp + MICmdInvoker.cpp + MICmdMgr.cpp + MICmdMgrSetCmdDeleteCallback.cpp + MICmnBase.cpp + MICmnLLDBBroadcaster.cpp + MICmnLLDBDebugger.cpp + MICmnLLDBDebuggerHandleEvents.cpp + MICmnLLDBDebugSessionInfo.cpp + MICmnLLDBDebugSessionInfoVarObj.cpp + MICmnLLDBProxySBValue.cpp + MICmnLLDBUtilSBValue.cpp + MICmnLog.cpp + MICmnLogMediumFile.cpp + MICmnMIOutOfBandRecord.cpp + MICmnMIResultRecord.cpp + MICmnMIValue.cpp + MICmnMIValueConst.cpp + MICmnMIValueList.cpp + MICmnMIValueResult.cpp + MICmnMIValueTuple.cpp + MICmnResources.cpp + MICmnStreamStderr.cpp + MICmnStreamStdin.cpp + MICmnStreamStdout.cpp + MICmnThreadMgrStd.cpp + MIDriver.cpp + MIDriverBase.cpp + MIDriverMain.cpp + MIDriverMgr.cpp + MIUtilDateTimeStd.cpp + MIUtilDebug.cpp + MIUtilFileStd.cpp + MIUtilMapIdToVariant.cpp + MIUtilString.cpp + MIUtilThreadBaseStd.cpp + MIUtilVariant.cpp +) + +set(llvm_deps "") + +find_library(lib_lldb NAMES lldb liblldb HINTS ${LLVM_LIBRARY_DIRS}) +find_library(lib_llvm LLVM HINTS ${LLVM_LIBRARY_DIRS}) + +if (NOT lib_llvm) + message(STATUS "Can't find LLVM shared library, falling back to static linking LLVMSupport") + find_library(lib_llvm LLVMSupport HINTS ${LLVM_LIBRARY_DIRS}) + + get_property(LLVMSupportDeps GLOBAL PROPERTY LLVMBUILD_LIB_DEPS_LLVMSupport) + foreach(dependency ${LLVMSupportDeps}) + find_library(lib ${dependency} HINTS ${LLVM_LIBRARY_DIRS}) + list(APPEND llvm_deps "${lib}") + endforeach() + if(NOT MSVC) + find_package(Curses REQUIRED) + list(APPEND llvm_deps "${CURSES_LIBRARIES}") + endif() +endif() + +list(APPEND llvm_deps "${LLVM_PTHREAD_LIB}") + +target_link_libraries(lldb-mi ${lib_lldb} ${lib_llvm} ${llvm_deps}) + diff --git a/src/MICmdArgContext.cpp b/src/MICmdArgContext.cpp new file mode 100644 index 00000000000..18da5b5d67a --- /dev/null +++ b/src/MICmdArgContext.cpp @@ -0,0 +1,221 @@ +//===-- MICmdArgContext.cpp -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmdArgContext.h" + +//++ +// Details: CMICmdArgContext constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgContext::CMICmdArgContext() {} + +//++ +// Details: CMICmdArgContext constructor. +// Type: Method. +// Args: vrCmdLineArgsRaw - (R) The text description of the arguments +// options. +// Return: None. +// Throws: None. +//-- +CMICmdArgContext::CMICmdArgContext(const CMIUtilString &vrCmdLineArgsRaw) + : m_strCmdArgsAndOptions(vrCmdLineArgsRaw) {} + +//++ +// Details: CMICmdArgContext destructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgContext::~CMICmdArgContext() {} + +//++ +// Details: Retrieve the remainder of the command's argument options left to +// parse. +// Type: Method. +// Args: None. +// Return: CMIUtilString & - Argument options text. +// Throws: None. +//-- +const CMIUtilString &CMICmdArgContext::GetArgsLeftToParse() const { + return m_strCmdArgsAndOptions; +} + +//++ +// Details: Ask if this arguments string has any arguments. +// Type: Method. +// Args: None. +// Return: bool - True = Has one or more arguments present, false = no +// arguments. +// Throws: None. +//-- +bool CMICmdArgContext::IsEmpty() const { + return m_strCmdArgsAndOptions.empty(); +} + +//++ +// Details: Remove the argument from the options text and any space after the +// argument +// if applicable. +// Type: Method. +// Args: vArg - (R) The name of the argument. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdArgContext::RemoveArg(const CMIUtilString &vArg) { + if (vArg.empty()) + return MIstatus::success; + + const size_t nLen = vArg.length(); + const size_t nLenCntxt = m_strCmdArgsAndOptions.length(); + if (nLen > nLenCntxt) + return MIstatus::failure; + + size_t nExtraSpace = 0; + size_t nPos = m_strCmdArgsAndOptions.find(vArg); + while (1) { + if (nPos == std::string::npos) + return MIstatus::success; + + bool bPass1 = false; + if (nPos != 0) { + if (m_strCmdArgsAndOptions[nPos - 1] == ' ') + bPass1 = true; + } else + bPass1 = true; + + const size_t nEnd = nPos + nLen; + + if (bPass1) { + bool bPass2 = false; + if (nEnd < nLenCntxt) { + if (m_strCmdArgsAndOptions[nEnd] == ' ') { + bPass2 = true; + nExtraSpace = 1; + } + } else + bPass2 = true; + + if (bPass2) + break; + } + + nPos = m_strCmdArgsAndOptions.find(vArg, nEnd); + } + + const size_t nPosEnd = nLen + nExtraSpace; + m_strCmdArgsAndOptions = m_strCmdArgsAndOptions.replace(nPos, nPosEnd, ""); + m_strCmdArgsAndOptions = m_strCmdArgsAndOptions.Trim(); + + return MIstatus::success; +} + +//++ +// Details: Remove the argument at the Nth word position along in the context +// string. +// Any space after the argument is removed if applicable. A search is +// not +// performed as there may be more than one vArg with the same 'name' in +// the +// context string. +// Type: Method. +// Args: vArg - (R) The name of the argument. +// nArgIndex - (R) The word count position to which to remove the +// vArg word. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdArgContext::RemoveArgAtPos(const CMIUtilString &vArg, + size_t nArgIndex) { + size_t nWordIndex = 0; + CMIUtilString strBuildContextUp; + const CMIUtilString::VecString_t vecWords(GetArgs()); + const bool bSpaceRequired(GetNumberArgsPresent() > 2); + + CMIUtilString::VecString_t::const_iterator it = vecWords.begin(); + const CMIUtilString::VecString_t::const_iterator itEnd = vecWords.end(); + while (it != itEnd) { + const CMIUtilString &rWord(*it); + if (nWordIndex++ != nArgIndex) { + // Single words + strBuildContextUp += rWord; + if (bSpaceRequired) + strBuildContextUp += " "; + } else { + // If quoted loose quoted text + if (++it != itEnd) { + CMIUtilString words = rWord; + while (vArg != words) { + if (bSpaceRequired) + words += " "; + words += *it; + if (++it == itEnd) + break; + } + if (it != itEnd) + --it; + } + } + + // Next + if (it != itEnd) + ++it; + } + + m_strCmdArgsAndOptions = strBuildContextUp; + m_strCmdArgsAndOptions = m_strCmdArgsAndOptions.Trim(); + + return MIstatus::success; +} + +//++ +// Details: Retrieve number of arguments or options present in the command's +// option text. +// Type: Method. +// Args: None. +// Return: size_t - 0 to n arguments present. +// Throws: None. +//-- +size_t CMICmdArgContext::GetNumberArgsPresent() const { + CMIUtilString::VecString_t vecOptions; + return m_strCmdArgsAndOptions.SplitConsiderQuotes(" ", vecOptions); +} + +//++ +// Details: Retrieve all the arguments or options remaining in *this context. +// Type: Method. +// Args: None. +// Return: MIUtilString::VecString_t - List of args remaining. +// Throws: None. +//-- +CMIUtilString::VecString_t CMICmdArgContext::GetArgs() const { + CMIUtilString::VecString_t vecOptions; + m_strCmdArgsAndOptions.SplitConsiderQuotes(" ", vecOptions); + return vecOptions; +} + +//++ +// Details: Copy assignment operator. +// Type: Method. +// Args: vOther - (R) The variable to copy from. +// Return: CMIUtilString & - this object. +// Throws: None. +//-- +CMICmdArgContext &CMICmdArgContext::operator=(const CMICmdArgContext &vOther) { + if (this != &vOther) { + m_strCmdArgsAndOptions = vOther.m_strCmdArgsAndOptions; + } + + return *this; +} diff --git a/src/MICmdArgContext.h b/src/MICmdArgContext.h new file mode 100644 index 00000000000..801d2d90cdc --- /dev/null +++ b/src/MICmdArgContext.h @@ -0,0 +1,43 @@ +//===-- MICmdArgContext.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MIUtilString.h" + +//++ +//============================================================================ +// Details: MI common code class. Command arguments and options string. Holds +// the context string. +// Based on the Interpreter pattern. +//-- +class CMICmdArgContext { + // Methods: +public: + /* ctor */ CMICmdArgContext(); + /* ctor */ CMICmdArgContext(const CMIUtilString &vrCmdLineArgsRaw); + // + const CMIUtilString &GetArgsLeftToParse() const; + size_t GetNumberArgsPresent() const; + CMIUtilString::VecString_t GetArgs() const; + bool IsEmpty() const; + bool RemoveArg(const CMIUtilString &vArg); + bool RemoveArgAtPos(const CMIUtilString &vArg, size_t nArgIndex); + // + CMICmdArgContext &operator=(const CMICmdArgContext &vOther); + + // Overridden: +public: + // From CMIUtilString + /* dtor */ virtual ~CMICmdArgContext(); + + // Attributes: +private: + CMIUtilString m_strCmdArgsAndOptions; +}; diff --git a/src/MICmdArgSet.cpp b/src/MICmdArgSet.cpp new file mode 100644 index 00000000000..0d67c03bfea --- /dev/null +++ b/src/MICmdArgSet.cpp @@ -0,0 +1,386 @@ +//===-- MICmdArgSet.cpp -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmdArgSet.h" +#include "MICmdArgValBase.h" +#include "MICmnLog.h" +#include "MICmnResources.h" + +//++ +// Details: CMICmdArgSet constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgSet::CMICmdArgSet() + : m_bIsArgsPresentButNotHandledByCmd(false), m_constStrCommaSpc(", ") {} + +//++ +// Details: CMICmdArgSet destructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgSet::~CMICmdArgSet() { + // Tidy up + Destroy(); +} + +//++ +// Details: Release resources used by *this container object. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMICmdArgSet::Destroy() { + // Delete command argument objects + if (!m_setCmdArgs.empty()) { + SetCmdArgs_t::iterator it = m_setCmdArgs.begin(); + while (it != m_setCmdArgs.end()) { + CMICmdArgValBase *pArg(*it); + delete pArg; + + // Next + ++it; + } + m_setCmdArgs.clear(); + } + + m_setCmdArgsThatNotValid.clear(); + m_setCmdArgsThatAreMissing.clear(); + m_setCmdArgsNotHandledByCmd.clear(); + m_setCmdArgsMissingInfo.clear(); + m_bIsArgsPresentButNotHandledByCmd = false; +} + +//++ +// Details: Retrieve the state flag indicating that the command set up ready to +// parse +// command arguments or options found that one or more arguments was +// indeed +// present but not handled. This is given as a warning in the MI log +// file. +// Type: Method. +// Args: None. +// Return: bool - True = one or more args not handled, false = all args handled +// Throws: None. +//-- +bool CMICmdArgSet::IsArgsPresentButNotHandledByCmd() const { + return m_bIsArgsPresentButNotHandledByCmd; +} + +//++ +// Details: Add the list of command's arguments to parse and validate another +// one. +// Type: Method. +// Args: vArg - (R) A command argument object. +// Return: None. +// Throws: None. +//-- +void CMICmdArgSet::Add(CMICmdArgValBase *vArg) { m_setCmdArgs.push_back(vArg); } + +//++ +// Details: After validating an options line of text (the context) and there is +// a failure, +// it is likely a mandatory command argument that is required is +// missing. This +// function returns the argument that should be present. +// Type: Method. +// Args: None. +// Return: SetCmdArgs_t & - Set of argument objects. +// Throws: None. +//-- +const CMICmdArgSet::SetCmdArgs_t &CMICmdArgSet::GetArgsThatAreMissing() const { + return m_setCmdArgsThatAreMissing; +} + +//++ +// Details: After validating an options line of text (the context) and there is +// a failure, +// it may be because one or more arguments were unable to extract a +// value. This +// function returns the argument that were found to be invalid. +// Type: Method. +// Args: None. +// Return: SetCmdArgs_t & - Set of argument objects. +// Throws: None. +//-- +const CMICmdArgSet::SetCmdArgs_t &CMICmdArgSet::GetArgsThatInvalid() const { + return m_setCmdArgsThatNotValid; +} + +//++ +// Details: The list of argument or option (objects) that were specified by the +// command +// and so recognised when parsed but were not handled. Ideally the +// command +// should handle all arguments and options presented to it. The command +// sends +// warning to the MI log file to say that these options were not +// handled. +// Used as one way to determine option that maybe should really be +// implemented +// and not just ignored. +// Type: Method. +// Args: None. +// Return: SetCmdArgs_t & - Set of argument objects. +// Throws: None. +//-- +const CMICmdArgSet::SetCmdArgs_t &CMICmdArgSet::GetArgsNotHandledByCmd() const { + return m_setCmdArgsNotHandledByCmd; +} + +//++ +// Details: Given a set of command argument objects parse the context option +// string to +// find those argument and retrieve their value. If the function fails +// call +// GetArgsThatAreMissing() to see which commands that were mandatory +// were +// missing or failed to parse. +// Type: Method. +// Args: vStrMiCmd - (R) Command's name. +// vCmdArgsText - (RW) A command's options or argument. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdArgSet::Validate(const CMIUtilString &vStrMiCmd, + CMICmdArgContext &vwCmdArgsText) { + m_cmdArgContext = vwCmdArgsText; + + // Iterate all the arguments or options required by a command + SetCmdArgs_t::const_iterator it = m_setCmdArgs.begin(); + while (it != m_setCmdArgs.end()) { + CMICmdArgValBase *pArg = *it; + + if (!pArg->Validate(vwCmdArgsText)) { + if (pArg->GetFound()) { + if (pArg->GetIsMissingOptions()) + m_setCmdArgsMissingInfo.push_back(pArg); + else if (!pArg->GetValid()) + m_setCmdArgsThatNotValid.push_back(pArg); + } else if (pArg->GetIsMandatory()) + m_setCmdArgsThatAreMissing.push_back(pArg); + } + + if (pArg->GetFound() && !pArg->GetIsHandledByCmd()) { + m_bIsArgsPresentButNotHandledByCmd = true; + m_setCmdArgsNotHandledByCmd.push_back(pArg); + } + + // Next + ++it; + } + + // report any issues with arguments/options + if (IsArgsPresentButNotHandledByCmd()) + WarningArgsNotHandledbyCmdLogFile(vStrMiCmd); + + return ValidationFormErrorMessages(vwCmdArgsText); +} + +//++ +// Details: Having validated the command's options text and failed for some +// reason form +// the error message made up with the faults found. +// Type: Method. +// vCmdArgsText - (RW) A command's options or argument. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdArgSet::ValidationFormErrorMessages( + const CMICmdArgContext &vwCmdArgsText) { + CMIUtilString strListMissing; + CMIUtilString strListInvalid; + CMIUtilString strListMissingInfo; + const bool bArgsMissing = (m_setCmdArgsThatAreMissing.size() > 0); + const bool bArgsInvalid = (m_setCmdArgsThatNotValid.size() > 0); + const bool bArgsMissingInfo = (m_setCmdArgsMissingInfo.size() > 0); + if (!(bArgsMissing || bArgsInvalid || bArgsMissingInfo)) + return MIstatus::success; + if (bArgsMissing) { + MIuint i = 0; + SetCmdArgs_t::const_iterator it = m_setCmdArgsThatAreMissing.begin(); + while (it != m_setCmdArgsThatAreMissing.end()) { + if (i++ > 0) + strListMissing += m_constStrCommaSpc; + + const CMICmdArgValBase *pArg(*it); + strListMissing += pArg->GetName(); + + // Next + ++it; + } + } + if (bArgsInvalid) { + MIuint i = 0; + SetCmdArgs_t::const_iterator it = m_setCmdArgsThatNotValid.begin(); + while (it != m_setCmdArgsThatNotValid.end()) { + if (i++ > 0) + strListMissing += m_constStrCommaSpc; + + const CMICmdArgValBase *pArg(*it); + strListInvalid += pArg->GetName(); + + // Next + ++it; + } + } + if (bArgsMissingInfo) { + MIuint i = 0; + SetCmdArgs_t::const_iterator it = m_setCmdArgsMissingInfo.begin(); + while (it != m_setCmdArgsMissingInfo.end()) { + if (i++ > 0) + strListMissingInfo += m_constStrCommaSpc; + + const CMICmdArgValBase *pArg(*it); + strListMissingInfo += pArg->GetName(); + + // Next + ++it; + } + } + + bool bHaveOneError = false; + CMIUtilString strError = MIRSRC(IDS_CMD_ARGS_ERR_PREFIX_MSG); + if (bArgsMissing && bArgsInvalid) { + bHaveOneError = true; + strError += + CMIUtilString::Format(MIRSRC(IDS_CMD_ARGS_ERR_VALIDATION_MAN_INVALID), + strListMissing.c_str(), strListInvalid.c_str()); + } + if (bArgsMissing) { + if (bHaveOneError) + strError += ". "; + bHaveOneError = true; + strError += CMIUtilString::Format( + MIRSRC(IDS_CMD_ARGS_ERR_VALIDATION_MANDATORY), strListMissing.c_str()); + } + if (bArgsMissingInfo) { + if (bHaveOneError) + strError += ". "; + bHaveOneError = true; + strError += + CMIUtilString::Format(MIRSRC(IDS_CMD_ARGS_ERR_VALIDATION_MISSING_INF), + strListMissingInfo.c_str()); + } + if (bArgsInvalid) { + if (bHaveOneError) + strError += ". "; + bHaveOneError = true; + strError += CMIUtilString::Format( + MIRSRC(IDS_CMD_ARGS_ERR_VALIDATION_INVALID), strListInvalid.c_str()); + } + if (!vwCmdArgsText.IsEmpty()) { + if (bHaveOneError) + strError += ". "; + bHaveOneError = true; + strError += + CMIUtilString::Format(MIRSRC(IDS_CMD_ARGS_ERR_CONTEXT_NOT_ALL_EATTEN), + vwCmdArgsText.GetArgsLeftToParse().c_str()); + } + + if (bHaveOneError) { + SetErrorDescription(strError); + return MIstatus::failure; + } + + return MIstatus::success; +} + +//++ +// Details: Ask if the command's argument options text had any arguments. +// Type: Method. +// Args: None. +// Return: bool - True = Has one or more arguments present, false = no +// arguments. +// Throws: None. +//-- +bool CMICmdArgSet::IsArgContextEmpty() const { + return m_cmdArgContext.IsEmpty(); +} + +//++ +// Details: Retrieve the number of arguments that are being used for the +// command. +// Type: Method. +// Args: None. +// Return: size_t - Argument count. +// Throws: None. +//-- +size_t CMICmdArgSet::GetCount() const { return m_setCmdArgs.size(); } + +//++ +// Details: Given a set of command argument objects retrieve the argument with +// the +// specified name. +// Type: Method. +// Args: vpArg - (W) A pointer to a command's argument object. +// Return: True - Argument found. +// False - Argument not found. +// Throws: None. +//-- +bool CMICmdArgSet::GetArg(const CMIUtilString &vArgName, + CMICmdArgValBase *&vpArg) const { + bool bFound = false; + SetCmdArgs_t::const_iterator it = m_setCmdArgs.begin(); + while (it != m_setCmdArgs.end()) { + CMICmdArgValBase *pArg(*it); + if (pArg->GetName() == vArgName) { + bFound = true; + vpArg = pArg; + break; + } + + // Next + ++it; + } + + return bFound; +} + +//++ +// Details: Write a warning message to the MI Log file about the command's +// arguments or +// options that were found present but not handled. +// Type: Method. +// Args: vrCmdName - (R) The command's name. +// Return: None. +// Throws: None. +//-- +void CMICmdArgSet::WarningArgsNotHandledbyCmdLogFile( + const CMIUtilString &vrCmdName) { +#if MICONFIG_GIVE_WARNING_CMD_ARGS_NOT_HANDLED + + CMIUtilString strArgsNotHandled; + const CMICmdArgSet::SetCmdArgs_t &rSetArgs = GetArgsNotHandledByCmd(); + MIuint nCnt = 0; + CMICmdArgSet::SetCmdArgs_t::const_iterator it = rSetArgs.begin(); + while (it != rSetArgs.end()) { + if (nCnt++ > 0) + strArgsNotHandled += m_constStrCommaSpc; + const CMICmdArgValBase *pArg = *it; + strArgsNotHandled += pArg->GetName(); + + // Next + ++it; + } + + const CMIUtilString strWarningMsg( + CMIUtilString::Format(MIRSRC(IDS_CMD_WRN_ARGS_NOT_HANDLED), + vrCmdName.c_str(), strArgsNotHandled.c_str())); + m_pLog->WriteLog(strWarningMsg); + +#endif // MICONFIG_GIVE_WARNING_CMD_ARGS_NOT_HANDLED +} diff --git a/src/MICmdArgSet.h b/src/MICmdArgSet.h new file mode 100644 index 00000000000..4df5aaf5156 --- /dev/null +++ b/src/MICmdArgSet.h @@ -0,0 +1,107 @@ +//===-- MICmdArgSet.h -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include + +#include "MICmdArgContext.h" +#include "MICmnBase.h" + +// Declarations: +class CMICmdArgValBase; + +//++ +//============================================================================ +// Details: MI common code class. Command arguments container class. +// A command may have one or more arguments of which some may be +// optional. +// *this class contains a list of the command's arguments which are +// validates against the commands argument options string (context +// string). +// Each argument tries to extract the value it is looking for. +// Argument objects added to *this container are owned by this +// container +// and are deleted when this container goes out of scope. Allocate +// argument +// objects on the heap. +// It is assumed the arguments to be parsed are read from left to right +// in +// order. The order added to *this container is the order they will +// parsed. +//-- +class CMICmdArgSet : public CMICmnBase { + // Classes: +public: + //++ + // Description: ArgSet's interface for command arguments to implement. + //-- + class IArg { + public: + virtual bool GetFound() const = 0; + virtual bool GetIsHandledByCmd() const = 0; + virtual bool GetIsMandatory() const = 0; + virtual bool GetIsMissingOptions() const = 0; + virtual const CMIUtilString &GetName() const = 0; + virtual bool GetValid() const = 0; + virtual bool Validate(CMICmdArgContext &vwArgContext) = 0; + + virtual ~IArg() = default; + }; + + // Typedefs: + typedef std::vector SetCmdArgs_t; + + // Methods: + CMICmdArgSet(); + + void Add(CMICmdArgValBase *vArg); + bool GetArg(const CMIUtilString &vArgName, CMICmdArgValBase *&vpArg) const; + const SetCmdArgs_t &GetArgsThatAreMissing() const; + const SetCmdArgs_t &GetArgsThatInvalid() const; + size_t GetCount() const; + bool IsArgContextEmpty() const; + bool IsArgsPresentButNotHandledByCmd() const; + void WarningArgsNotHandledbyCmdLogFile(const CMIUtilString &vrCmdName); + bool Validate(const CMIUtilString &vStrMiCmd, + CMICmdArgContext &vwCmdArgsText); + + // Overrideable: + ~CMICmdArgSet() override; + + // Methods: +private: + const SetCmdArgs_t &GetArgsNotHandledByCmd() const; + void Destroy(); // Release resources used by *this object + bool ValidationFormErrorMessages(const CMICmdArgContext &vwCmdArgsText); + + // Attributes: + bool m_bIsArgsPresentButNotHandledByCmd; // True = The driver's client + // presented the command with options + // recognised but not handled by + // a command, false = all args handled + SetCmdArgs_t m_setCmdArgs; // The set of arguments that are that the command + // is expecting to find in the options string + SetCmdArgs_t m_setCmdArgsThatAreMissing; // The set of arguments that are + // required by the command but are + // missing + SetCmdArgs_t m_setCmdArgsThatNotValid; // The set of arguments found in the + // text but for some reason unable to + // extract a value + SetCmdArgs_t m_setCmdArgsNotHandledByCmd; // The set of arguments specified by + // the command which were present to + // the command but not handled + SetCmdArgs_t m_setCmdArgsMissingInfo; // The set of arguments that were + // present but were found to be missing + // additional information i.e. + // --thread 3 but 3 is missing + CMICmdArgContext m_cmdArgContext; // Copy of the command's argument options + // text before validate takes place (empties + // it of content) + const CMIUtilString m_constStrCommaSpc; +}; diff --git a/src/MICmdArgValBase.cpp b/src/MICmdArgValBase.cpp new file mode 100644 index 00000000000..dc9c7e0257e --- /dev/null +++ b/src/MICmdArgValBase.cpp @@ -0,0 +1,129 @@ +//===-- MICmdArgValBase.cpp -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "MICmdArgValBase.h" +#include "MICmdArgContext.h" +#include "MIUtilString.h" + +//++ +// Details: CMICmdArgValBase constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgValBase::CMICmdArgValBase() + : m_bFound(false), m_bValid(false), m_bMandatory(false), m_bHandled(false), + m_bIsMissingOptions(false) {} + +//++ +// Details: CMICmdArgValBase constructor. +// Type: Method. +// Args: vrArgName - (R) Argument's name to search by. +// vbMandatory - (R) True = Yes must be present, false = optional +// argument. +// vbHandleByCmd - (R) True = Command processes *this option, false = +// not handled. +// Return: None. +// Throws: None. +//-- +CMICmdArgValBase::CMICmdArgValBase(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd) + : m_bFound(false), m_bValid(false), m_bMandatory(vbMandatory), + m_strArgName(vrArgName), m_bHandled(vbHandleByCmd), + m_bIsMissingOptions(false) {} + +//++ +// Details: Retrieve the state flag of whether the argument is handled by the +// command or +// not. +// Type: Method. +// Args: None. +// Return: True - Command needs more information. +// False - All information is present as expected. +// Throws: None. +//-- +bool CMICmdArgValBase::GetIsMissingOptions() const { + return m_bIsMissingOptions; +} + +//++ +// Details: Retrieve the state flag of whether the argument is handled by the +// command or +// not. +// Type: Method. +// Args: None. +// Return: True - Command handles *this argument or option. +// False - Not handled (argument specified but ignored). +// Throws: None. +//-- +bool CMICmdArgValBase::GetIsHandledByCmd() const { return m_bHandled; } + +//++ +// Details: Retrieve the name of *this argument. +// Type: Method. +// Args: None. +// Return: CMIUtilString & - Return the text name. +// Throws: None. +//-- +const CMIUtilString &CMICmdArgValBase::GetName() const { return m_strArgName; } + +//++ +// Details: Retrieve the state flag of whether the argument was found in the +// command's +// argument / options string. +// Type: Method. +// Args: None. +// Return: True - Argument found. +// False - Argument not found. +// Throws: None. +//-- +bool CMICmdArgValBase::GetFound() const { return m_bFound; } + +//++ +// Details: Retrieve the state flag indicating whether the value was obtained +// from the +// text arguments string and is valid. +// Type: Method. +// Args: None. +// Return: True - Argument valid. +// False - Argument not valid. +// Throws: None. +//-- +bool CMICmdArgValBase::GetValid() const { return m_bValid; } + +//++ +// Details: Retrieve the state flag indicating whether *this argument is a +// mandatory +// argument for the command or is optional to be present. +// Type: Method. +// Args: None. +// Return: True - Mandatory. +// False - Optional. +// Throws: None. +//-- +bool CMICmdArgValBase::GetIsMandatory() const { return m_bMandatory; } + +//++ +// Details: Parse the command's argument options string and try to extract the +// value *this +// argument is looking for. +// Type: Overrideable. +// Args: vArgContext - (RW) The command's argument options string. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdArgValBase::Validate(CMICmdArgContext &vwArgContext) { + MIunused(vwArgContext); + + // Override to implement + + return MIstatus::failure; +} diff --git a/src/MICmdArgValBase.h b/src/MICmdArgValBase.h new file mode 100644 index 00000000000..feb7fe4f04d --- /dev/null +++ b/src/MICmdArgValBase.h @@ -0,0 +1,115 @@ +//===-- MICmdArgValBase.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include "MICmdArgSet.h" +#include "MIUtilString.h" + +//++ +//============================================================================ +// Details: MI common code class. Command argument base class. Arguments objects +// needing specialization derived from *this class. An argument knows +// what type of argument it is and how it is to interpret the options +// (context) string to find and validate a matching argument and so +// extract a value from it. +// Argument objects are added to the CMICmdArgSet container object. +// Once added the container they belong to that contain and will be +// deleted when the container goes out of scope. Allocate argument +// objects on the heap and pass in to the Add(). +// Note the code is written such that a command will produce an error +// should it be presented with arguments or options it does not +// understand. +// A command can recognise an option or argument then ignore if it +// wishes (a warning is sent to the MI's Log file). This is so it is +// hardwired to fail and catch arguments or options that presented by +// different driver clients. +// Based on the Interpreter pattern. +//-- +class CMICmdArgValBase : public CMICmdArgSet::IArg { + // Methods: +public: + CMICmdArgValBase(); + CMICmdArgValBase(const CMIUtilString &vrArgName, const bool vbMandatory, + const bool vbHandleByCmd); + + // Overrideable: + ~CMICmdArgValBase() override = default; + + // Overridden: + // From CMICmdArgSet::IArg + bool GetFound() const override; + bool GetIsHandledByCmd() const override; + bool GetIsMandatory() const override; + bool GetIsMissingOptions() const override; + const CMIUtilString &GetName() const override; + bool GetValid() const override; + bool Validate(CMICmdArgContext &vwArgContext) override; + + // Attributes: +protected: + bool + m_bFound; // True = yes found in arguments options text, false = not found + bool m_bValid; // True = yes argument parsed and valid, false = not valid + bool + m_bMandatory; // True = yes arg must be present, false = optional argument + CMIUtilString m_strArgName; + bool m_bHandled; // True = Command processes *this option, false = not handled + bool m_bIsMissingOptions; // True = Command needs more information, false = ok +}; + +//++ +//============================================================================ +// Details: MI common code class. Templated command argument base class. +//-- +template class CMICmdArgValBaseTemplate : public CMICmdArgValBase { + // Methods: +public: + CMICmdArgValBaseTemplate() = default; + CMICmdArgValBaseTemplate(const CMIUtilString &vrArgName, + const bool vbMandatory, const bool vbHandleByCmd); + // + const T &GetValue() const; + + // Overrideable: + ~CMICmdArgValBaseTemplate() override = default; + + // Attributes: +protected: + T m_argValue; +}; + +//++ +// Details: CMICmdArgValBaseTemplate constructor. +// Type: Method. +// Args: vrArgName - (R) Argument's name to search by. +// vbMandatory - (R) True = Yes must be present, false = optional +// argument. +// vbHandleByCmd - (R) True = Command processes *this option, false = +// not handled. +// Return: None. +// Throws: None. +//-- +template +CMICmdArgValBaseTemplate::CMICmdArgValBaseTemplate( + const CMIUtilString &vrArgName, const bool vbMandatory, + const bool vbHandleByCmd) + : CMICmdArgValBase(vrArgName, vbMandatory, vbHandleByCmd) {} + +//++ +// Details: Retrieve the value the argument parsed from the command's argument / +// options +// text string. +// Type: Method. +// Args: None. +// Return: Template type & - The arg value of *this object. +// Throws: None. +//-- +template const T &CMICmdArgValBaseTemplate::GetValue() const { + return m_argValue; +} diff --git a/src/MICmdArgValConsume.cpp b/src/MICmdArgValConsume.cpp new file mode 100644 index 00000000000..01d00136659 --- /dev/null +++ b/src/MICmdArgValConsume.cpp @@ -0,0 +1,88 @@ +//===-- MICmdArgValConsume.cpp ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmdArgValConsume.h" +#include "MICmdArgContext.h" + +//++ +// Details: CMICmdArgValConsume constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgValConsume::CMICmdArgValConsume() {} + +//++ +// Details: CMICmdArgValConsume constructor. +// Type: Method. +// Args: vrArgName - (R) Argument's name to search by. +// vbMandatory - (R) True = Yes must be present, false = optional +// argument. +// Return: None. +// Throws: None. +//-- +CMICmdArgValConsume::CMICmdArgValConsume(const CMIUtilString &vrArgName, + const bool vbMandatory) + : CMICmdArgValBaseTemplate(vrArgName, vbMandatory, true) {} + +//++ +// Details: CMICmdArgValConsume destructor. +// Type: Overidden. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgValConsume::~CMICmdArgValConsume() {} + +//++ +// Details: Parse the command's argument options string and try to extract the +// value *this +// argument is looking for. +// Type: Overridden. +// Args: vwArgContext - (R) The command's argument options string. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdArgValConsume::Validate(CMICmdArgContext &vwArgContext) { + if (vwArgContext.IsEmpty()) + return m_bMandatory ? MIstatus::failure : MIstatus::success; + + // Consume the optional file, line, linenum arguments till the mode '--' + // argument + const CMIUtilString::VecString_t vecOptions(vwArgContext.GetArgs()); + CMIUtilString::VecString_t::const_iterator it = vecOptions.begin(); + while (it != vecOptions.end()) { + const CMIUtilString &rTxt(*it); + + if (rTxt == "--") { + m_bFound = true; + m_bValid = true; + if (!vwArgContext.RemoveArg(rTxt)) + return MIstatus::failure; + return MIstatus::success; + } + // Next + ++it; + } + + return MIstatus::failure; +} + +//++ +// Details: Nothing to examine as we just want to consume the argument or option +// (ignore +// it). +// Type: Method. +// Args: None. +// Return: bool - True = yes ok, false = not ok. +// Throws: None. +//-- +bool CMICmdArgValConsume::IsOk() const { return true; } diff --git a/src/MICmdArgValConsume.h b/src/MICmdArgValConsume.h new file mode 100644 index 00000000000..62207f04b67 --- /dev/null +++ b/src/MICmdArgValConsume.h @@ -0,0 +1,53 @@ +//===-- MICmdArgValConsume.h ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmdArgValBase.h" + +// Declarations: +class CMICmdArgContext; + +//++ +//============================================================================ +// Details: MI common code class. Command argument class. Arguments object +// needing specialization derived from the CMICmdArgValBase class. +// An argument knows what type of argument it is and how it is to +// interpret the options (context) string to find and validate a +// matching +// argument. This type having recognised its argument name just +// consumes +// that argument or option (ignores it). This is the so the validation +// process can then ask if all arguments or options have been +// recognised +// other an error will occurred "argument not recognised". For example +// this can be used to consume the "--" text which is not an argument +// in +// itself. Normally the GetValue() function (in base class) would +// return +// a value for the argument but is not the case for *this argument type +// object. +// Based on the Interpreter pattern. +//-- +class CMICmdArgValConsume : public CMICmdArgValBaseTemplate { + // Methods: +public: + /* ctor */ CMICmdArgValConsume(); + /* ctor */ CMICmdArgValConsume(const CMIUtilString &vrArgName, + const bool vbMandatory); + // + bool IsOk() const; + + // Overridden: +public: + // From CMICmdArgValBase + /* dtor */ ~CMICmdArgValConsume() override; + // From CMICmdArgSet::IArg + bool Validate(CMICmdArgContext &vwArgContext) override; +}; diff --git a/src/MICmdArgValFile.cpp b/src/MICmdArgValFile.cpp new file mode 100644 index 00000000000..7171b0fa8f1 --- /dev/null +++ b/src/MICmdArgValFile.cpp @@ -0,0 +1,178 @@ +//===-- MICmdArgValFile.cpp -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmdArgValFile.h" +#include "MICmdArgContext.h" + +//++ +// Details: CMICmdArgValFile constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgValFile::CMICmdArgValFile() {} + +//++ +// Details: CMICmdArgValFile constructor. +// Type: Method. +// Args: vrArgName - (R) Argument's name to search by. +// vbMandatory - (R) True = Yes must be present, false = optional +// argument. +// vbHandleByCmd - (R) True = Command processes *this option, false = +// not handled. +// Return: None. +// Throws: None. +//-- +CMICmdArgValFile::CMICmdArgValFile(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd) + : CMICmdArgValBaseTemplate(vrArgName, vbMandatory, vbHandleByCmd) {} + +//++ +// Details: CMICmdArgValFile destructor. +// Type: Overridden. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgValFile::~CMICmdArgValFile() {} + +//++ +// Details: Parse the command's argument options string and try to extract the +// value *this +// argument is looking for. +// Type: Overridden. +// Args: vwArgContext - (R) The command's argument options string. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdArgValFile::Validate(CMICmdArgContext &vwArgContext) { + if (vwArgContext.IsEmpty()) + return m_bMandatory ? MIstatus::failure : MIstatus::success; + + // The GDB/MI spec suggests there is only parameter + + if (vwArgContext.GetNumberArgsPresent() == 1) { + const CMIUtilString &rFile(vwArgContext.GetArgsLeftToParse()); + if (IsFilePath(rFile)) { + m_bFound = true; + m_bValid = true; + m_argValue = rFile.Trim('"'); + vwArgContext.RemoveArg(rFile); + return MIstatus::success; + } else + return MIstatus::failure; + } + + // In reality there are more than one option, if so the file option + // is the last one (don't handle that here - find the best looking one) + const CMIUtilString::VecString_t vecOptions(vwArgContext.GetArgs()); + CMIUtilString::VecString_t::const_iterator it = vecOptions.begin(); + while (it != vecOptions.end()) { + const CMIUtilString &rTxt(*it); + if (IsFilePath(rTxt)) { + m_bFound = true; + + if (vwArgContext.RemoveArg(rTxt)) { + m_bValid = true; + m_argValue = rTxt.Trim('"'); + return MIstatus::success; + } else + return MIstatus::success; + } + + // Next + ++it; + } + + return MIstatus::failure; +} + +//++ +// Details: Given some text extract the file name path from it. If a space is +// found in +// path done return the path surrounded in quotes. +// Type: Method. +// Args: vrTxt - (R) The text to extract the file name path from. +// Return: CMIUtilString - File name and or path. +// Throws: None. +//-- +CMIUtilString +CMICmdArgValFile::GetFileNamePath(const CMIUtilString &vrTxt) const { + CMIUtilString fileNamePath(vrTxt); + + // Look for a space in the path + const char cSpace = ' '; + const size_t nPos = fileNamePath.find(cSpace); + if (nPos != std::string::npos) + fileNamePath = CMIUtilString::Format("\"%s\"", fileNamePath.c_str()); + + return fileNamePath; +} + +//++ +// Details: Examine the string and determine if it is a valid file name path. +// Type: Method. +// Args: vrFileNamePath - (R) File's name and directory path. +// Return: bool - True = yes valid file path, false = no. +// Throws: None. +//-- +bool CMICmdArgValFile::IsFilePath(const CMIUtilString &vrFileNamePath) const { + if (vrFileNamePath.empty()) + return false; + + const bool bHavePosSlash = (vrFileNamePath.find('/') != std::string::npos); + const bool bHaveBckSlash = (vrFileNamePath.find('\\') != std::string::npos); + + // Look for --someLongOption + size_t nPos = vrFileNamePath.find("--"); + const bool bLong = (nPos == 0); + if (bLong) + return false; + + // Look for -f type short parameters + nPos = vrFileNamePath.find('-'); + const bool bShort = (nPos == 0); + if (bShort) + return false; + + // Look for i1 i2 i3.... + nPos = vrFileNamePath.find('i'); + const bool bFoundI1 = ((nPos == 0) && (::isdigit(vrFileNamePath[1]))); + if (bFoundI1) + return false; + + const bool bValidChars = IsValidChars(vrFileNamePath); + return bValidChars || bHavePosSlash || bHaveBckSlash; +} + +//++ +// Details: Determine if the path contains valid characters for a file path. +// Letters can be +// either upper or lower case. +// Type: Method. +// Args: vrText - (R) The text data to examine. +// Return: bool - True = yes valid, false = one or more chars is valid. +// Throws: None. +//-- +bool CMICmdArgValFile::IsValidChars(const CMIUtilString &vrText) const { + static CMIUtilString s_strSpecialCharacters(".'\"`@#$%^&*()_+-={}[]| "); + const char *pPtr = vrText.c_str(); + for (MIuint i = 0; i < vrText.length(); i++, pPtr++) { + const char c = *pPtr; + if (::isalnum((int)c) == 0) { + if (s_strSpecialCharacters.find(c) == CMIUtilString::npos) + return false; + } + } + + return true; +} diff --git a/src/MICmdArgValFile.h b/src/MICmdArgValFile.h new file mode 100644 index 00000000000..8030cb783aa --- /dev/null +++ b/src/MICmdArgValFile.h @@ -0,0 +1,47 @@ +//===-- MICmdArgValFile.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmdArgValBase.h" + +// Declarations: +class CMICmdArgContext; + +//++ +//============================================================================ +// Details: MI common code class. Command argument class. Arguments object +// needing specialization derived from the CMICmdArgValBase class. +// An argument knows what type of argument it is and how it is to +// interpret the options (context) string to find and validate a +// matching +// argument and so extract a value from it . +// Based on the Interpreter pattern. +//-- +class CMICmdArgValFile : public CMICmdArgValBaseTemplate { + // Methods: +public: + /* ctor */ CMICmdArgValFile(); + /* ctor */ CMICmdArgValFile(const CMIUtilString &vrArgName, + const bool vbMandatory, const bool vbHandleByCmd); + // + bool IsFilePath(const CMIUtilString &vrFileNamePath) const; + CMIUtilString GetFileNamePath(const CMIUtilString &vrTxt) const; + + // Overridden: +public: + // From CMICmdArgValBase + /* dtor */ ~CMICmdArgValFile() override; + // From CMICmdArgSet::IArg + bool Validate(CMICmdArgContext &vwArgContext) override; + + // Methods: +private: + bool IsValidChars(const CMIUtilString &vrText) const; +}; diff --git a/src/MICmdArgValListBase.cpp b/src/MICmdArgValListBase.cpp new file mode 100644 index 00000000000..bd175f3afe6 --- /dev/null +++ b/src/MICmdArgValListBase.cpp @@ -0,0 +1,209 @@ +//===-- MICmdArgValListBase.cpp ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmdArgValListBase.h" +#include "MICmdArgContext.h" +#include "MICmdArgValConsume.h" +#include "MICmdArgValFile.h" +#include "MICmdArgValNumber.h" +#include "MICmdArgValOptionLong.h" +#include "MICmdArgValOptionShort.h" +#include "MICmdArgValString.h" +#include "MICmdArgValThreadGrp.h" + +//++ +// Details: CMICmdArgValListBase constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgValListBase::CMICmdArgValListBase() + : m_eArgType(eArgValType_invalid) {} + +//++ +// Details: CMICmdArgValListBase constructor. +// Type: Method. +// Args: vrArgName - (R) Argument's name to search by. +// vbMandatory - (R) True = Yes must be present, false = optional +// argument. +// vbHandleByCmd - (R) True = Command processes *this option, false = +// not handled. +// Return: None. +// Throws: None. +//-- +CMICmdArgValListBase::CMICmdArgValListBase(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd) + : CMICmdArgValBaseTemplate(vrArgName, vbMandatory, vbHandleByCmd), + m_eArgType(eArgValType_invalid) {} + +//++ +// Details: CMICmdArgValListBase constructor. +// Type: Method. +// Args: vrArgName - (R) Argument's name to search by. +// vbMandatory - (R) True = Yes must be present, false = optional +// argument. +// vbHandleByCmd - (R) True = Command processes *this option, false = +// not handled. +// veType - (R) The type of argument to look for and create +// argument object of a certain type. +// Return: None. +// Throws: None. +//-- +CMICmdArgValListBase::CMICmdArgValListBase(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd, + const ArgValType_e veType) + : CMICmdArgValBaseTemplate(vrArgName, vbMandatory, vbHandleByCmd), + m_eArgType(veType) {} + +//++ +// Details: CMICmdArgValListBase destructor. +// Type: Overridden. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgValListBase::~CMICmdArgValListBase() { + // Tidy up + Destroy(); +} + +//++ +// Details: Tear down resources used by *this object. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMICmdArgValListBase::Destroy() { + // Tidy up + VecArgObjPtr_t::const_iterator it = m_argValue.begin(); + while (it != m_argValue.end()) { + CMICmdArgValBase *pArgObj = *it; + delete pArgObj; + + // Next + ++it; + } + m_argValue.clear(); +} + +//++ +// Details: Create an CMICmdArgValBase derived object matching the type +// specified +// and put the option or argument's value inside it. +// Type: Method. +// Args: vrTxt - (R) Text version the option or argument. +// veType - (R) The type of argument or option object to create. +// Return: CMICmdArgValBase * - Option object holding the value. +// - NULL = Functional failed. +// Throws: None. +//-- +CMICmdArgValBase * +CMICmdArgValListBase::CreationObj(const CMIUtilString &vrTxt, + const ArgValType_e veType) const { + CMICmdArgValBase *pOptionObj = nullptr; + switch (veType) { + case eArgValType_File: + pOptionObj = new CMICmdArgValFile(); + break; + case eArgValType_Consume: + pOptionObj = new CMICmdArgValConsume(); + break; + case eArgValType_Number: + pOptionObj = new CMICmdArgValNumber(); + break; + case eArgValType_OptionLong: + pOptionObj = new CMICmdArgValOptionLong(); + break; + case eArgValType_OptionShort: + pOptionObj = new CMICmdArgValOptionShort(); + break; + case eArgValType_String: + pOptionObj = new CMICmdArgValString(); + break; + case eArgValType_StringQuoted: + pOptionObj = new CMICmdArgValString(true, false, false); + break; + case eArgValType_StringQuotedNumber: + pOptionObj = new CMICmdArgValString(true, true, false); + break; + case eArgValType_StringQuotedNumberPath: + pOptionObj = new CMICmdArgValString(true, true, true); + break; + case eArgValType_StringAnything: + pOptionObj = new CMICmdArgValString(true); + break; + case eArgValType_ThreadGrp: + pOptionObj = new CMICmdArgValThreadGrp(); + break; + default: + return nullptr; + } + + CMICmdArgContext argCntxt(vrTxt); + if (!pOptionObj->Validate(argCntxt)) + return nullptr; + + return pOptionObj; +} + +//++ +// Details: Validate the option or argument is the correct type. +// Type: Method. +// Args: vrTxt - (R) Text version the option or argument. +// veType - (R) The type of value to expect. +// Return: bool - True = Yes expected type present, False = no. +// Throws: None. +//-- +bool CMICmdArgValListBase::IsExpectedCorrectType( + const CMIUtilString &vrTxt, const ArgValType_e veType) const { + bool bValid = false; + switch (veType) { + case eArgValType_File: + bValid = CMICmdArgValFile().IsFilePath(vrTxt); + break; + case eArgValType_Consume: + bValid = CMICmdArgValConsume().IsOk(); + break; + case eArgValType_Number: + bValid = CMICmdArgValNumber().IsArgNumber(vrTxt); + break; + case eArgValType_OptionLong: + bValid = CMICmdArgValOptionLong().IsArgLongOption(vrTxt); + break; + case eArgValType_OptionShort: + bValid = CMICmdArgValOptionShort().IsArgShortOption(vrTxt); + break; + case eArgValType_String: + bValid = CMICmdArgValString().IsStringArg(vrTxt); + break; + case eArgValType_StringQuoted: + bValid = CMICmdArgValString(true, false, false).IsStringArg(vrTxt); + break; + case eArgValType_StringQuotedNumber: + bValid = CMICmdArgValString(true, true, false).IsStringArg(vrTxt); + break; + case eArgValType_StringQuotedNumberPath: + bValid = CMICmdArgValString(true, true, true).IsStringArg(vrTxt); + break; + case eArgValType_StringAnything: + bValid = CMICmdArgValString(true).IsStringArg(vrTxt); + break; + case eArgValType_ThreadGrp: + bValid = CMICmdArgValThreadGrp().IsArgThreadGrp(vrTxt); + break; + default: + return false; + } + + return bValid; +} diff --git a/src/MICmdArgValListBase.h b/src/MICmdArgValListBase.h new file mode 100644 index 00000000000..4437ae19a87 --- /dev/null +++ b/src/MICmdArgValListBase.h @@ -0,0 +1,101 @@ +//===-- MICmdArgValListBase.h -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// Third party headers: +#include + +// In-house headers: +#include "MICmdArgValBase.h" + +// Declarations: +class CMICmdArgContext; + +//++ +//============================================================================ +// Details: MI common code class. Command argument with addition options class. +// For example --recurse 1 2 4 [group ...]. Arguments object that +// require a list of options associated with them derive from the +// CMICmdArgValListBase class. Additional options are also extracted +// from +// the command arguments text string. +// An argument knows what type of argument it is and how it is to +// interpret the options (context) string to find and validate a +// matching +// options and so extract a values from it . +// The CMICmdArgValBase objects are added to the derived argument +// class's +// container. The option arguments belong to that derived class and +// will +// be deleted that object goes out of scope. +// Based on the Interpreter pattern. +//-- +class CMICmdArgValListBase + : public CMICmdArgValBaseTemplate> { + // Typedef: +public: + typedef std::vector VecArgObjPtr_t; + + // Enums: +public: + //++ + // Details: CMICmdArgValListBase needs to know what type of argument to look + // for in + // the command options text. It also needs to create argument objects + // of + // a specific type. + //-- + enum ArgValType_e { + eArgValType_File = 0, + eArgValType_Consume, + eArgValType_Number, + eArgValType_OptionLong, + eArgValType_OptionShort, + eArgValType_String, + eArgValType_StringQuoted, + eArgValType_StringQuotedNumber, + eArgValType_StringQuotedNumberPath, + eArgValType_StringAnything, // Accept any words for a string 'type' even if + // they look like --longOptions for example + eArgValType_ThreadGrp, + eArgValType_count, // Always the last one + eArgValType_invalid + }; + + // Methods: +public: + /* ctor */ CMICmdArgValListBase(); + /* ctor */ CMICmdArgValListBase(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd); + /* ctor */ CMICmdArgValListBase(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd, + const ArgValType_e veType); + + // Overridden: +public: + // From CMICmdArgValBase + /* dtor */ ~CMICmdArgValListBase() override; + + // Methods: +protected: + bool IsExpectedCorrectType(const CMIUtilString &vrTxt, + const ArgValType_e veType) const; + CMICmdArgValBase *CreationObj(const CMIUtilString &vrTxt, + const ArgValType_e veType) const; + + // Attributes: +protected: + ArgValType_e m_eArgType; + + // Methods: +private: + void Destroy(); +}; diff --git a/src/MICmdArgValListOfN.cpp b/src/MICmdArgValListOfN.cpp new file mode 100644 index 00000000000..b53424543b2 --- /dev/null +++ b/src/MICmdArgValListOfN.cpp @@ -0,0 +1,167 @@ +//===-- MICmdArgValListOfN.cpp ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmdArgValListOfN.h" +#include "MICmdArgContext.h" +#include "MICmdArgValFile.h" +#include "MICmdArgValNumber.h" +#include "MICmdArgValOptionLong.h" +#include "MICmdArgValOptionShort.h" +#include "MICmdArgValString.h" +#include "MICmdArgValThreadGrp.h" + +//++ +// Details: CMICmdArgValListOfN constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgValListOfN::CMICmdArgValListOfN() {} + +//++ +// Details: CMICmdArgValListOfN constructor. +// Type: Method. +// Args: vrArgName - (R) Argument's name to search by. +// vbMandatory - (R) True = Yes must be present, false = optional +// argument. +// vbHandleByCmd - (R) True = Command processes *this option, false = +// not handled. +// veType - (R) The type of argument to look for and create +// argument object of a certain type. +// Return: None. +// Throws: None. +//-- +CMICmdArgValListOfN::CMICmdArgValListOfN(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd, + const ArgValType_e veType) + : CMICmdArgValListBase(vrArgName, vbMandatory, vbHandleByCmd, veType) {} + +//++ +// Details: CMICmdArgValListOfN destructor. +// Type: Overridden. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgValListOfN::~CMICmdArgValListOfN() {} + +//++ +// Details: Parse the command's argument options string and try to extract the +// list of +// arguments based on the argument object type to look for. +// Type: Overridden. +// Args: vwArgContext - (RW) The command's argument options string. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdArgValListOfN::Validate(CMICmdArgContext &vwArgContext) { + if (m_eArgType >= eArgValType_count) { + m_eArgType = eArgValType_invalid; + return MIstatus::failure; + } + + if (vwArgContext.IsEmpty()) + return m_bMandatory ? MIstatus::failure : MIstatus::success; + + const CMIUtilString &rArg(vwArgContext.GetArgsLeftToParse()); + if (IsListOfN(rArg) && CreateList(rArg)) { + m_bFound = true; + m_bValid = true; + vwArgContext.RemoveArg(rArg); + return MIstatus::success; + } else + return MIstatus::failure; +} + +//++ +// Details: Create list of argument objects each holding a value extract from +// the command +// options line. +// Type: Method. +// Args: vrTxt - (R) Some options text. +// Return: bool - True = yes valid arg, false = no. +// Throws: None. +//-- +bool CMICmdArgValListOfN::CreateList(const CMIUtilString &vrTxt) { + CMIUtilString::VecString_t vecOptions; + if ((m_eArgType == eArgValType_StringQuoted) || + (m_eArgType == eArgValType_StringQuotedNumber) || + (m_eArgType == eArgValType_StringQuotedNumberPath) || + (m_eArgType == eArgValType_StringAnything)) { + if (vrTxt.SplitConsiderQuotes(" ", vecOptions) == 0) + return MIstatus::failure; + } else if (vrTxt.Split(" ", vecOptions) == 0) + return MIstatus::failure; + + CMIUtilString::VecString_t::const_iterator it = vecOptions.begin(); + while (it != vecOptions.end()) { + const CMIUtilString &rOption = *it; + CMICmdArgValBase *pOption = CreationObj(rOption, m_eArgType); + if (pOption != nullptr) + m_argValue.push_back(pOption); + else + return MIstatus::failure; + + // Next + ++it; + } + + return MIstatus::success; +} + +//++ +// Details: Examine the string and determine if it is a valid string type +// argument. +// Type: Method. +// Args: vrTxt - (R) Some text. +// Return: bool - True = yes valid arg, false = no. +// Throws: None. +//-- +bool CMICmdArgValListOfN::IsListOfN(const CMIUtilString &vrTxt) const { + CMIUtilString::VecString_t vecOptions; + if ((m_eArgType == eArgValType_StringQuoted) || + (m_eArgType == eArgValType_StringQuotedNumber) || + (m_eArgType == eArgValType_StringQuotedNumberPath) || + (m_eArgType == eArgValType_StringAnything)) { + if (vrTxt.SplitConsiderQuotes(" ", vecOptions) == 0) + return false; + } else if (vrTxt.Split(" ", vecOptions) == 0) + return false; + + CMIUtilString::VecString_t::const_iterator it = vecOptions.begin(); + while (it != vecOptions.end()) { + const CMIUtilString &rOption = *it; + if (!IsExpectedCorrectType(rOption, m_eArgType)) + break; + + // Next + ++it; + } + + return true; +} + +//++ +// Details: Retrieve the list of CMICmdArgValBase derived option objects found +// following +// *this long option argument. For example "list-thread-groups [ +// --recurse 1 ]" +// where 1 is the list of expected option to follow. +// Type: Method. +// Args: None. +// Return: CMICmdArgValListBase::VecArgObjPtr_t & - List of options. +// Throws: None. +//-- +const CMICmdArgValListBase::VecArgObjPtr_t & +CMICmdArgValListOfN::GetExpectedOptions() const { + return m_argValue; +} diff --git a/src/MICmdArgValListOfN.h b/src/MICmdArgValListOfN.h new file mode 100644 index 00000000000..74e8c16f7d6 --- /dev/null +++ b/src/MICmdArgValListOfN.h @@ -0,0 +1,92 @@ +//===-- MICmdArgValListOfN.h ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// Third party headers: +#include + +// In-house headers: +#include "MICmdArgValListBase.h" + +// Declarations: +class CMICmdArgContext; + +//++ +//============================================================================ +// Details: MI common code class. Command argument class. Arguments object +// needing specialization derived from the CMICmdArgValBase class. +// An argument knows what type of argument it is and how it is to +// interpret the options (context) string to find and validate a +// matching +// argument and so extract a value from it . +// The CMICmdArgValBase objects added to *this ListOfN container belong +// to this container and will be deleted when *this object goes out of +// scope. +// To parse arguments like 'thread-id ...' i.e. 1 10 12 13 ... +// If vbMandatory argument is true it takes on the (...)+ specification +// otherwise assumed to be (...)* specification. +// Based on the Interpreter pattern. +//-- +class CMICmdArgValListOfN : public CMICmdArgValListBase { + // Methods: +public: + /* ctor */ CMICmdArgValListOfN(); + /* ctor */ CMICmdArgValListOfN(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd, + const ArgValType_e veType); + // + const VecArgObjPtr_t &GetExpectedOptions() const; + template + bool GetExpectedOption(T2 &vrwValue, + const VecArgObjPtr_t::size_type vnAt = 0) const; + + // Overridden: +public: + // From CMICmdArgValBase + /* dtor */ ~CMICmdArgValListOfN() override; + // From CMICmdArgSet::IArg + bool Validate(CMICmdArgContext &vArgContext) override; + + // Methods: +private: + bool IsListOfN(const CMIUtilString &vrTxt) const; + bool CreateList(const CMIUtilString &vrTxt); +}; + +//++ +// Details: Retrieve the first argument or option value from the list of 1 or +// more options +// parsed from the command's options string. +// Type: Template method. +// Args: vrwValue - (W) Templated type return value. +// vnAt - (R) Value at the specific position. +// T1 - The argument value's class type of the data hold in +// the list of options. +// T2 - The type pf the variable which holds the value wanted. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. List of object was empty. +// Throws: None. +//-- +template +bool CMICmdArgValListOfN::GetExpectedOption( + T2 &vrwValue, const VecArgObjPtr_t::size_type vnAt) const { + const VecArgObjPtr_t &rVecOptions(GetExpectedOptions()); + if (rVecOptions.size() <= vnAt) + return MIstatus::failure; + + VecArgObjPtr_t::const_iterator it2 = rVecOptions.begin() + vnAt; + if (it2 != rVecOptions.end()) { + const T1 *pOption = static_cast(*it2); + vrwValue = pOption->GetValue(); + return MIstatus::success; + } + + return MIstatus::failure; +} diff --git a/src/MICmdArgValNumber.cpp b/src/MICmdArgValNumber.cpp new file mode 100644 index 00000000000..ad2df79bf3d --- /dev/null +++ b/src/MICmdArgValNumber.cpp @@ -0,0 +1,156 @@ +//===-- MICmdArgValNumber.cpp -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmdArgValNumber.h" +#include "MICmdArgContext.h" + +//++ +// Details: CMICmdArgValNumber constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgValNumber::CMICmdArgValNumber() + : m_nNumberFormatMask(CMICmdArgValNumber::eArgValNumberFormat_Decimal), + m_nNumber(0) {} + +//++ +// Details: CMICmdArgValNumber constructor. +// Type: Method. +// Args: vrArgName - (R) Argument's name to search by. +// vbMandatory - (R) True = Yes must be present, false = +// optional argument. +// vbHandleByCmd - (R) True = Command processes *this option, +// false = not handled. +// vnNumberFormatMask - (R) Mask of the number formats. (Dflt = +// CMICmdArgValNumber::eArgValNumberFormat_Decimal) +// Return: None. +// Throws: None. +//-- +CMICmdArgValNumber::CMICmdArgValNumber( + const CMIUtilString &vrArgName, const bool vbMandatory, + const bool vbHandleByCmd, + const MIuint + vnNumberFormatMask /* = CMICmdArgValNumber::eArgValNumberFormat_Decimal*/) + : CMICmdArgValBaseTemplate(vrArgName, vbMandatory, vbHandleByCmd), + m_nNumberFormatMask(vnNumberFormatMask), m_nNumber(0) {} + +//++ +// Details: CMICmdArgValNumber destructor. +// Type: Overridden. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgValNumber::~CMICmdArgValNumber() {} + +//++ +// Details: Parse the command's argument options string and try to extract the +// value *this +// argument is looking for. +// Type: Overridden. +// Args: vwArgContext - (RW) The command's argument options string. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdArgValNumber::Validate(CMICmdArgContext &vwArgContext) { + if (vwArgContext.IsEmpty()) + return m_bMandatory ? MIstatus::failure : MIstatus::success; + + if (vwArgContext.GetNumberArgsPresent() == 1) { + const CMIUtilString &rArg(vwArgContext.GetArgsLeftToParse()); + if (IsArgNumber(rArg) && ExtractNumber(rArg)) { + m_bFound = true; + m_bValid = true; + m_argValue = GetNumber(); + vwArgContext.RemoveArg(rArg); + return MIstatus::success; + } else + return MIstatus::failure; + } + + // More than one option... + const CMIUtilString::VecString_t vecOptions(vwArgContext.GetArgs()); + CMIUtilString::VecString_t::const_iterator it = vecOptions.begin(); + while (it != vecOptions.end()) { + const CMIUtilString &rArg(*it); + if (IsArgNumber(rArg) && ExtractNumber(rArg)) { + m_bFound = true; + + if (vwArgContext.RemoveArg(rArg)) { + m_bValid = true; + m_argValue = GetNumber(); + return MIstatus::success; + } else + return MIstatus::failure; + } + + // Next + ++it; + } + + return MIstatus::failure; +} + +//++ +// Details: Examine the string and determine if it is a valid string type +// argument. +// Type: Method. +// Args: vrTxt - (R) Some text. +// Return: bool - True = yes valid arg, false = no. +// Throws: None. +//-- +bool CMICmdArgValNumber::IsArgNumber(const CMIUtilString &vrTxt) const { + const bool bFormatDecimal(m_nNumberFormatMask & + CMICmdArgValNumber::eArgValNumberFormat_Decimal); + const bool bFormatHexadecimal( + m_nNumberFormatMask & + CMICmdArgValNumber::eArgValNumberFormat_Hexadecimal); + + // Look for --someLongOption + if (std::string::npos != vrTxt.find("--")) + return false; + + if (bFormatDecimal && vrTxt.IsNumber()) + return true; + + if (bFormatHexadecimal && vrTxt.IsHexadecimalNumber()) + return true; + + return false; +} + +//++ +// Details: Extract the thread group number from the thread group argument. +// Type: Method. +// Args: vrTxt - (R) Some text. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdArgValNumber::ExtractNumber(const CMIUtilString &vrTxt) { + MIint64 nNumber = 0; + bool bOk = vrTxt.ExtractNumber(nNumber); + if (bOk) { + m_nNumber = static_cast(nNumber); + } + + return bOk; +} + +//++ +// Details: Retrieve the thread group ID found in the argument. +// Type: Method. +// Args: None. +// Return: MIuint - Thread group ID. +// Throws: None. +//-- +MIint64 CMICmdArgValNumber::GetNumber() const { return m_nNumber; } diff --git a/src/MICmdArgValNumber.h b/src/MICmdArgValNumber.h new file mode 100644 index 00000000000..23c888e5d80 --- /dev/null +++ b/src/MICmdArgValNumber.h @@ -0,0 +1,69 @@ +//===-- MICmdArgValNumber.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmdArgValBase.h" + +// Declarations: +class CMICmdArgContext; + +//++ +//============================================================================ +// Details: MI common code class. Command argument class. Arguments object +// needing specialization derived from the CMICmdArgValBase class. +// An argument knows what type of argument it is and how it is to +// interpret the options (context) string to find and validate a +// matching +// argument and so extract a value from it . +// Based on the Interpreter pattern. +//-- +class CMICmdArgValNumber : public CMICmdArgValBaseTemplate { + // Enums: +public: + //++ + // Details: CMICmdArgValNumber needs to know what format of argument to look + // for in + // the command options text. + //-- + enum ArgValNumberFormat_e { + eArgValNumberFormat_Decimal = (1u << 0), + eArgValNumberFormat_Hexadecimal = (1u << 1), + eArgValNumberFormat_Auto = + ((eArgValNumberFormat_Hexadecimal << 1) - + 1u) ///< Indicates to try and lookup everything up during a query. + }; + + // Methods: +public: + /* ctor */ CMICmdArgValNumber(); + /* ctor */ CMICmdArgValNumber( + const CMIUtilString &vrArgName, const bool vbMandatory, + const bool vbHandleByCmd, + const MIuint vnNumberFormatMask = eArgValNumberFormat_Decimal); + // + bool IsArgNumber(const CMIUtilString &vrTxt) const; + + // Overridden: +public: + // From CMICmdArgValBase + /* dtor */ ~CMICmdArgValNumber() override; + // From CMICmdArgSet::IArg + bool Validate(CMICmdArgContext &vwArgContext) override; + + // Methods: +private: + bool ExtractNumber(const CMIUtilString &vrTxt); + MIint64 GetNumber() const; + + // Attributes: +private: + MIuint m_nNumberFormatMask; + MIint64 m_nNumber; +}; diff --git a/src/MICmdArgValOptionLong.cpp b/src/MICmdArgValOptionLong.cpp new file mode 100644 index 00000000000..3dc9d40f4b5 --- /dev/null +++ b/src/MICmdArgValOptionLong.cpp @@ -0,0 +1,291 @@ +//===-- MICmdArgValOptionLong.cpp -------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmdArgValOptionLong.h" +#include "MICmdArgContext.h" + +//++ +// Details: CMICmdArgValOptionLong constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgValOptionLong::CMICmdArgValOptionLong() + : m_nExpectingNOptions(0), m_eExpectingOptionType(eArgValType_invalid) {} + +//++ +// Details: CMICmdArgValOptionLong constructor. +// Type: Method. +// Args: vrArgName - (R) Argument's name to search by. +// vbMandatory - (R) True = Yes must be present, false = optional +// argument. +// vbHandleByCmd - (R) True = Command processes *this option, false = +// not handled. +// Return: None. +// Throws: None. +//-- +CMICmdArgValOptionLong::CMICmdArgValOptionLong(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd) + : CMICmdArgValListBase(vrArgName, vbMandatory, vbHandleByCmd), + m_nExpectingNOptions(0), m_eExpectingOptionType(eArgValType_invalid) {} + +//++ +// Details: CMICmdArgValOptionLong constructor. +// Type: Method. +// Args: vrArgName - (R) Argument's name to search by. +// vbMandatory - (R) True = Yes must be present, false = +// optional argument. +// vbHandleByCmd - (R) True = Command processes *this option, +// false = not handled. +// veType - (R) The type of argument to look for and +// create argument object of a certain type. +// vnExpectingNOptions - (R) The number of options expected to read +// following *this argument. +// Return: None. +// Throws: None. +//-- +CMICmdArgValOptionLong::CMICmdArgValOptionLong(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd, + const ArgValType_e veType, + const MIuint vnExpectingNOptions) + : CMICmdArgValListBase(vrArgName, vbMandatory, vbHandleByCmd), + m_nExpectingNOptions(vnExpectingNOptions), + m_eExpectingOptionType(veType) {} + +//++ +// Details: CMICmdArgValOptionLong destructor. +// Type: Overridden. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgValOptionLong::~CMICmdArgValOptionLong() { + // Tidy up + Destroy(); +} + +//++ +// Details: Tear down resources used by *this object. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMICmdArgValOptionLong::Destroy() { + // Tidy up + VecArgObjPtr_t::const_iterator it = m_vecArgsExpected.begin(); + while (it != m_vecArgsExpected.end()) { + CMICmdArgValBase *pOptionObj = *it; + delete pOptionObj; + + // Next + ++it; + } + m_vecArgsExpected.clear(); +} + +//++ +// Details: Parse the command's argument options string and try to extract the +// long +// argument *this argument type is looking for. +// Type: Overridden. +// Args: vwArgContext - (RW) The command's argument options string. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdArgValOptionLong::Validate(CMICmdArgContext &vwArgContext) { + if (vwArgContext.IsEmpty()) + return m_bMandatory ? MIstatus::failure : MIstatus::success; + + if (vwArgContext.GetNumberArgsPresent() == 1) { + const CMIUtilString &rArg(vwArgContext.GetArgsLeftToParse()); + if (IsArgLongOption(rArg) && ArgNameMatch(rArg)) { + m_bFound = true; + + if (!vwArgContext.RemoveArg(rArg)) + return MIstatus::failure; + + if (m_nExpectingNOptions == 0) { + m_bValid = true; + return MIstatus::success; + } + + m_bIsMissingOptions = true; + return MIstatus::failure; + } else + return MIstatus::failure; + } + + // More than one option... + MIuint nArgIndex = 0; + const CMIUtilString::VecString_t vecOptions(vwArgContext.GetArgs()); + CMIUtilString::VecString_t::const_iterator it = vecOptions.begin(); + while (it != vecOptions.end()) { + const CMIUtilString &rArg(*it); + if (IsArgOptionCorrect(rArg) && ArgNameMatch(rArg)) { + m_bFound = true; + + if (!vwArgContext.RemoveArg(rArg)) + return MIstatus::failure; + + if (m_nExpectingNOptions != 0) { + if (ExtractExpectedOptions(vwArgContext, nArgIndex)) { + m_bValid = true; + return MIstatus::success; + } + + m_bIsMissingOptions = true; + return MIstatus::failure; + } else { + m_bValid = true; + return MIstatus::success; + } + } + + // Next + ++it; + ++nArgIndex; + } + + return MIstatus::failure; +} + +//++ +// Details: Parse the text following *this argument and extract the options the +// values of +// CMICmdArgValListBase::m_eArgType forming argument objects for each +// of those +// options extracted. +// Type: Method. +// Args: vrwTxt - (RW) The command's argument options string. +// nArgIndex - (R) The Nth arg position in argument context from +// the left. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdArgValOptionLong::ExtractExpectedOptions(CMICmdArgContext &vrwTxt, + const MIuint nArgIndex) { + CMIUtilString::VecString_t vecOptions = vrwTxt.GetArgs(); + if (vecOptions.size() == 0) + return MIstatus::failure; + + MIuint nArgIndexCnt = 0; + MIuint nTypeCnt = 0; + MIuint nTypeCnt2 = 0; + MIuint nFoundNOptionsCnt = 0; + CMIUtilString::VecString_t::const_iterator it = vecOptions.begin(); + while (it != vecOptions.end()) { + // Move to the Nth argument position from left before do validation/checking + if (nArgIndexCnt++ == nArgIndex) { + nTypeCnt++; + const CMIUtilString &rOption(*it); + if (IsExpectedCorrectType(rOption, m_eExpectingOptionType)) { + nTypeCnt2++; + CMICmdArgValBase *pOptionObj = + CreationObj(rOption, m_eExpectingOptionType); + if ((pOptionObj != nullptr) && + vrwTxt.RemoveArgAtPos(rOption, nArgIndex)) { + nFoundNOptionsCnt++; + m_vecArgsExpected.push_back(pOptionObj); + } + } + + // Is the sequence 'options' of same type broken. Expecting the same type + // until the + // next argument. + if (nTypeCnt != nTypeCnt2) + return MIstatus::failure; + + if (nFoundNOptionsCnt == m_nExpectingNOptions) + return MIstatus::success; + } + + // Next + ++it; + } + if (nFoundNOptionsCnt != m_nExpectingNOptions) + return MIstatus::failure; + + return MIstatus::success; +} + +//++ +// Details: Examine the string and determine if it is a valid long type option +// argument. +// Long type argument looks like --someLongOption. +// Type: Method. +// Args: vrTxt - (R) Some text. +// Return: bool - True = yes valid arg, false = no. +// Throws: None. +//-- +bool CMICmdArgValOptionLong::IsArgLongOption(const CMIUtilString &vrTxt) const { + const bool bHavePosSlash = (vrTxt.find('/') != std::string::npos); + const bool bHaveBckSlash = (vrTxt.find('\\') != std::string::npos); + if (bHavePosSlash || bHaveBckSlash) + return false; + + const size_t nPos = vrTxt.find("--"); + if (nPos != 0) + return false; + + if (vrTxt.length() < 3) + return false; + + const CMIUtilString strArg = vrTxt.substr(2); + return !strArg.IsNumber(); +} + +//++ +// Details: Examine the string and determine if it is a valid long type option +// argument. +// Long type argument looks like --someLongOption. +// Type: Overideable. +// Args: vrTxt - (R) Some text. +// Return: bool - True = yes valid arg, false = no. +// Throws: None. +//-- +bool CMICmdArgValOptionLong::IsArgOptionCorrect( + const CMIUtilString &vrTxt) const { + return IsArgLongOption(vrTxt); +} + +//++ +// Details: Does the argument name of the argument being parsed ATM match the +// name of +// *this argument object. +// Type: Method. +// Args: vrTxt - (R) Some text. +// Return: bool - True = yes arg name matched, false = no. +// Throws: None. +//-- +bool CMICmdArgValOptionLong::ArgNameMatch(const CMIUtilString &vrTxt) const { + const CMIUtilString strArg = vrTxt.substr(2); + return (strArg == GetName()); +} + +//++ +// Details: Retrieve the list of CMICmdArgValBase derived option objects found +// following +// *this long option argument. For example "list-thread-groups [ +// --recurse 1 ]" +// where 1 is the list of expected option to follow. +// Type: Method. +// Args: None. +// Return: CMICmdArgValListBase::VecArgObjPtr_t & - List of options. +// Throws: None. +//-- +const CMICmdArgValListBase::VecArgObjPtr_t & +CMICmdArgValOptionLong::GetExpectedOptions() const { + return m_vecArgsExpected; +} diff --git a/src/MICmdArgValOptionLong.h b/src/MICmdArgValOptionLong.h new file mode 100644 index 00000000000..24082973368 --- /dev/null +++ b/src/MICmdArgValOptionLong.h @@ -0,0 +1,104 @@ +//===-- MICmdArgValOptionLong.h ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmdArgValListBase.h" + +// Declarations: +class CMICmdArgContext; +class CMIUtilString; + +//++ +//============================================================================ +// Details: MI common code class. Command argument class. Arguments object +// needing specialization derived from the CMICmdArgValBase class. +// An argument knows what type of argument it is and how it is to +// interpret the options (context) string to find and validate a +// matching +// argument and so extract a value from it. +// If *this argument has expected options following it the option +// objects +// created to hold each of those option's values belong to *this +// argument +// object and so are deleted when *this object goes out of scope. +// Based on the Interpreter pattern. +//-- +class CMICmdArgValOptionLong : public CMICmdArgValListBase { + // Methods: +public: + /* ctor */ CMICmdArgValOptionLong(); + /* ctor */ CMICmdArgValOptionLong(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd); + /* ctor */ CMICmdArgValOptionLong(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd, + const ArgValType_e veType, + const MIuint vnExpectingNOptions); + // + bool IsArgLongOption(const CMIUtilString &vrTxt) const; + const VecArgObjPtr_t &GetExpectedOptions() const; + template bool GetExpectedOption(T2 &vrwValue) const; + + // Overridden: +public: + // From CMICmdArgValBase + /* dtor */ ~CMICmdArgValOptionLong() override; + // From CMICmdArgSet::IArg + bool Validate(CMICmdArgContext &vArgContext) override; + + // Methods: +protected: + bool ExtractExpectedOptions(CMICmdArgContext &vrwTxt, const MIuint nArgIndex); + + // Overrideable: +protected: + virtual bool IsArgOptionCorrect(const CMIUtilString &vrTxt) const; + virtual bool ArgNameMatch(const CMIUtilString &vrTxt) const; + + // Methods: +private: + void Destroy(); + + // Attributes: +private: + MIuint m_nExpectingNOptions; // The number of options expected to read + // following *this argument + VecArgObjPtr_t m_vecArgsExpected; // The option objects holding the value + // extracted following *this argument + ArgValType_e m_eExpectingOptionType; // The type of options expected to read + // following *this argument +}; + +//++ +// Details: Retrieve the first argument or option value from the list of 1 or +// more options +// parsed from the command's options string. +// Type: Template method. +// Args: vrwValue - (W) Templated type return value. +// T1 - The argument value's class type of the data hold in +// the list of options. +// T2 - The type pf the variable which holds the value wanted. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. List of object was empty. +// Throws: None. +//-- +template +bool CMICmdArgValOptionLong::GetExpectedOption(T2 &vrwValue) const { + const VecArgObjPtr_t &rVecOptions(GetExpectedOptions()); + VecArgObjPtr_t::const_iterator it2 = rVecOptions.begin(); + if (it2 != rVecOptions.end()) { + const T1 *pOption = static_cast(*it2); + vrwValue = pOption->GetValue(); + return MIstatus::success; + } + + return MIstatus::failure; +} diff --git a/src/MICmdArgValOptionShort.cpp b/src/MICmdArgValOptionShort.cpp new file mode 100644 index 00000000000..7b7116cd5f1 --- /dev/null +++ b/src/MICmdArgValOptionShort.cpp @@ -0,0 +1,121 @@ +//===-- MICmdArgValOptionShort.cpp ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmdArgValOptionShort.h" +#include "MICmdArgContext.h" + +//++ +// Details: CMICmdArgValOptionShort constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgValOptionShort::CMICmdArgValOptionShort() {} + +//++ +// Details: CMICmdArgValOptionShort constructor. +// Type: Method. +// Args: vrArgName - (R) Argument's name to search by. +// vbMandatory - (R) True = Yes must be present, false = optional +// argument. +// vbHandleByCmd - (R) True = Command processes *this option, false = +// not handled. +// Return: None. +// Throws: None. +//-- +CMICmdArgValOptionShort::CMICmdArgValOptionShort(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd) + : CMICmdArgValOptionLong(vrArgName, vbMandatory, vbHandleByCmd) {} + +//++ +// Details: CMICmdArgValOptionLong constructor. +// Type: Method. +// Args: vrArgName - (R) Argument's name to search by. +// vbMandatory - (R) True = Yes must be present, false = +// optional argument. +// vbHandleByCmd - (R) True = Command processes *this option, +// false = not handled. +// veType - (R) The type of argument to look for and +// create argument object of a certain type. +// vnExpectingNOptions - (R) The number of options expected to read +// following *this argument. +// Return: None. +// Throws: None. +//-- +CMICmdArgValOptionShort::CMICmdArgValOptionShort( + const CMIUtilString &vrArgName, const bool vbMandatory, + const bool vbHandleByCmd, const ArgValType_e veType, + const MIuint vnExpectingNOptions) + : CMICmdArgValOptionLong(vrArgName, vbMandatory, vbHandleByCmd, veType, + vnExpectingNOptions) {} + +//++ +// Details: CMICmdArgValOptionShort destructor. +// Type: Overridden. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgValOptionShort::~CMICmdArgValOptionShort() {} + +//++ +// Details: Examine the string and determine if it is a valid short type option +// argument. +// Type: Method. +// Args: vrTxt - (R) Some text. +// Return: bool - True = yes valid arg, false = no. +// Throws: None. +//-- +bool CMICmdArgValOptionShort::IsArgShortOption( + const CMIUtilString &vrTxt) const { + // Look for --someLongOption + MIint nPos = vrTxt.find("--"); + if (nPos == 0) + return false; + + // Look for -f short option + nPos = vrTxt.find('-'); + if (nPos != 0) + return false; + + if (vrTxt.length() > 2) + return false; + + return true; +} + +//++ +// Details: Examine the string and determine if it is a valid short type option +// argument. +// Long type argument looks like -f some short option. +// Type: Overridden. +// Args: vrTxt - (R) Some text. +// Return: bool - True = yes valid arg, false = no. +// Throws: None. +//-- +bool CMICmdArgValOptionShort::IsArgOptionCorrect( + const CMIUtilString &vrTxt) const { + return IsArgShortOption(vrTxt); +} + +//++ +// Details: Does the argument name of the argument being parsed ATM match the +// name of +// *this argument object. +// Type: Overridden. +// Args: vrTxt - (R) Some text. +// Return: bool - True = yes arg name matched, false = no. +// Throws: None. +//-- +bool CMICmdArgValOptionShort::ArgNameMatch(const CMIUtilString &vrTxt) const { + const CMIUtilString strArg = vrTxt.substr(1); + return (strArg == GetName()); +} diff --git a/src/MICmdArgValOptionShort.h b/src/MICmdArgValOptionShort.h new file mode 100644 index 00000000000..fd39c9e73a5 --- /dev/null +++ b/src/MICmdArgValOptionShort.h @@ -0,0 +1,59 @@ +//===-- MICmdArgValOptionShort.h --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmdArgValOptionLong.h" + +// Declarations: +class CMICmdArgContext; +class CMIUtilString; + +//++ +//============================================================================ +// Details: MI common code class. Command argument class. Arguments object +// needing specialization derived from the CMICmdArgValOptionLong +// class. +// An argument knows what type of argument it is and how it is to +// interpret the options (context) string to find and validate a +// matching +// argument and so extract a value from it. +// If *this argument has expected options following it the option +// objects +// created to hold each of those option's values belong to *this +// argument +// object and so are deleted when *this object goes out of scope. +// Based on the Interpreter pattern. +//-- +class CMICmdArgValOptionShort : public CMICmdArgValOptionLong { + // Methods: +public: + /* ctor */ CMICmdArgValOptionShort(); + /* ctor */ CMICmdArgValOptionShort(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd); + /* ctor */ CMICmdArgValOptionShort(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd, + const ArgValType_e veType, + const MIuint vnExpectingNOptions); + // + bool IsArgShortOption(const CMIUtilString &vrTxt) const; + + // Overridden: +public: + // From CMICmdArgValBase + /* dtor */ ~CMICmdArgValOptionShort() override; + + // Overridden: +private: + // From CMICmdArgValOptionLong + bool IsArgOptionCorrect(const CMIUtilString &vrTxt) const override; + bool ArgNameMatch(const CMIUtilString &vrTxt) const override; +}; diff --git a/src/MICmdArgValPrintValues.cpp b/src/MICmdArgValPrintValues.cpp new file mode 100644 index 00000000000..a2ce4524737 --- /dev/null +++ b/src/MICmdArgValPrintValues.cpp @@ -0,0 +1,125 @@ +//===-- MICmdArgValPrintValues.cpp ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmdArgValPrintValues.h" +#include "MICmdArgContext.h" + +//++ +// Details: CMICmdArgValPrintValues constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgValPrintValues::CMICmdArgValPrintValues() : m_nPrintValues(0) {} + +//++ +// Details: CMICmdArgValPrintValues constructor. +// Type: Method. +// Args: vrArgName - (R) Argument's name to search by. +// vbMandatory - (R) True = Yes must be present, false = optional +// argument. +// vbHandleByCmd - (R) True = Command processes *this option, false = +// not handled. +// Return: None. +// Throws: None. +//-- +CMICmdArgValPrintValues::CMICmdArgValPrintValues(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd) + : CMICmdArgValBaseTemplate(vrArgName, vbMandatory, vbHandleByCmd), + m_nPrintValues(0) {} + +//++ +// Details: CMICmdArgValPrintValues destructor. +// Type: Overridden. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgValPrintValues::~CMICmdArgValPrintValues() {} + +//++ +// Details: Parse the command's argument options string and try to extract the +// value *this +// argument is looking for. +// Type: Overridden. +// Args: vwArgContext - (RW) The command's argument options string. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdArgValPrintValues::Validate(CMICmdArgContext &vwArgContext) { + if (vwArgContext.IsEmpty()) + return m_bMandatory ? MIstatus::failure : MIstatus::success; + + const CMIUtilString strArg(vwArgContext.GetArgs()[0]); + if (IsArgPrintValues(strArg) && ExtractPrintValues(strArg)) { + m_bFound = true; + m_bValid = true; + m_argValue = GetPrintValues(); + vwArgContext.RemoveArg(strArg); + return MIstatus::success; + } + + return MIstatus::failure; +} + +//++ +// Details: Examine the string and determine if it is a valid string type +// argument. +// Type: Method. +// Args: vrTxt - (R) Some text. +// Return: bool - True = yes valid arg, false = no. +// Throws: None. +//-- +bool CMICmdArgValPrintValues::IsArgPrintValues( + const CMIUtilString &vrTxt) const { + return (CMIUtilString::Compare(vrTxt, "0") || + CMIUtilString::Compare(vrTxt, "--no-values") || + CMIUtilString::Compare(vrTxt, "1") || + CMIUtilString::Compare(vrTxt, "--all-values") || + CMIUtilString::Compare(vrTxt, "2") || + CMIUtilString::Compare(vrTxt, "--simple-values")); +} + +//++ +// Details: Extract the print-values from the print-values argument. +// Type: Method. +// Args: vrTxt - (R) Some text. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdArgValPrintValues::ExtractPrintValues(const CMIUtilString &vrTxt) { + if (CMIUtilString::Compare(vrTxt, "0") || + CMIUtilString::Compare(vrTxt, "--no-values")) + m_nPrintValues = 0; + else if (CMIUtilString::Compare(vrTxt, "1") || + CMIUtilString::Compare(vrTxt, "--all-values")) + m_nPrintValues = 1; + else if (CMIUtilString::Compare(vrTxt, "2") || + CMIUtilString::Compare(vrTxt, "--simple-values")) + m_nPrintValues = 2; + else + return MIstatus::failure; + + return MIstatus::success; +} + +//++ +// Details: Retrieve the print-values found in the argument. +// Type: Method. +// Args: None. +// Return: MIuint - The print-values. +// Throws: None. +//-- +MIuint CMICmdArgValPrintValues::GetPrintValues() const { + return m_nPrintValues; +} diff --git a/src/MICmdArgValPrintValues.h b/src/MICmdArgValPrintValues.h new file mode 100644 index 00000000000..3fa8142c1ae --- /dev/null +++ b/src/MICmdArgValPrintValues.h @@ -0,0 +1,56 @@ +//===-- MICmdArgValPrintValues.h --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmdArgValBase.h" + +// Declarations: +class CMICmdArgContext; + +//++ +//============================================================================ +// Details: MI common code class. Command argument class. Arguments object +// needing specialization derived from the CMICmdArgValBase class. +// An argument knows what type of argument it is and how it is to +// interpret the options (context) string to find and validate a +// matching +// argument and so extract a value from it. The print-values looks +// like: +// 0 or --no-values +// 1 or --all-values +// 2 or --simple-values +// Based on the Interpreter pattern. +//-- +class CMICmdArgValPrintValues : public CMICmdArgValBaseTemplate { + // Methods: +public: + /* ctor */ CMICmdArgValPrintValues(); + /* ctor */ CMICmdArgValPrintValues(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd); + // + bool IsArgPrintValues(const CMIUtilString &vrTxt) const; + + // Overridden: +public: + // From CMICmdArgValBase + /* dtor */ ~CMICmdArgValPrintValues() override; + // From CMICmdArgSet::IArg + bool Validate(CMICmdArgContext &vArgContext) override; + + // Methods: +private: + bool ExtractPrintValues(const CMIUtilString &vrTxt); + MIuint GetPrintValues() const; + + // Attributes: +private: + MIuint m_nPrintValues; +}; diff --git a/src/MICmdArgValString.cpp b/src/MICmdArgValString.cpp new file mode 100644 index 00000000000..bd105bc289b --- /dev/null +++ b/src/MICmdArgValString.cpp @@ -0,0 +1,380 @@ +//===-- MICmdArgValString.cpp -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmdArgValString.h" +#include "MICmdArgContext.h" + +//++ +// Details: CMICmdArgValString constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgValString::CMICmdArgValString() + : m_bHandleQuotedString(false), m_bAcceptNumbers(false), + m_bHandleDirPaths(false), m_bHandleAnything(false) {} + +//++ +// Details: CMICmdArgValString constructor. +// Type: Method. +// Args: vbAnything - (R) True = Parse a string and accept anything, false = +// do not accept anything. +// Return: None. +// Throws: None. +//-- +CMICmdArgValString::CMICmdArgValString(const bool vbAnything) + : m_bHandleQuotedString(vbAnything), m_bAcceptNumbers(false), + m_bHandleDirPaths(false), m_bHandleAnything(vbAnything) {} + +//++ +// Details: CMICmdArgValString constructor. +// Type: Method. +// Args: vbHandleQuotes - (R) True = Parse a string surrounded by quotes +// spaces are not delimiters, false = only text up to +// next delimiting space character. +// vbAcceptNumbers - (R) True = Parse a string and accept as a +// number if number, false = numbers not recognised +// as string types. +// vbHandleDirPaths - (R) True = Parse a string and accept as a file +// path if a path, false = file paths are not +// recognised as string types. +// Return: None. +// Throws: None. +//-- +CMICmdArgValString::CMICmdArgValString(const bool vbHandleQuotes, + const bool vbAcceptNumbers, + const bool vbHandleDirPaths) + : m_bHandleQuotedString(vbHandleQuotes), m_bAcceptNumbers(vbAcceptNumbers), + m_bHandleDirPaths(vbHandleDirPaths), m_bHandleAnything(false) {} + +//++ +// Details: CMICmdArgValString constructor. +// Type: Method. +// Args: vrArgName - (R) Argument's name to search by. +// vbMandatory - (R) True = Yes must be present, false = optional +// argument. +// vbHandleByCmd - (R) True = Command processes *this option, false = +// not handled. +// vbHandleQuotes - (R) True = Parse a string surrounded by quotes +// spaces are not delimiters, false = only text up to +// next delimiting space character. (Dflt = false) +// vbAcceptNumbers - (R) True = Parse a string and accept as a number +// if number, false = numbers not recognised as +// string types. (Dflt = false) +// Return: None. +// Throws: None. +//-- +CMICmdArgValString::CMICmdArgValString(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd, + const bool vbHandleQuotes /* = false */, + const bool vbAcceptNumbers /* = false */) + : CMICmdArgValBaseTemplate(vrArgName, vbMandatory, vbHandleByCmd), + m_bHandleQuotedString(vbHandleQuotes), m_bAcceptNumbers(vbAcceptNumbers), + m_bHandleDirPaths(false), m_bHandleAnything(false) {} + +//++ +// Details: CMICmdArgValString constructor. +// Type: Method. +// Args: vrArgName - (R) Argument's name to search by. +// vbMandatory - (R) True = Yes must be present, false = optional +// argument. +// vbHandleByCmd - (R) True = Command processes *this option, false = +// not handled. +// vbHandleQuotes - (R) True = Parse a string surrounded by quotes +// spaces are not delimiters, false = only text up to +// next delimiting space character. +// vbAcceptNumbers - (R) True = Parse a string and accept as a number +// if number, false = numbers not recognised as +// vbHandleDirPaths - (R) True = Parse a string and accept as a file +// path if a path, false = file paths are not +// string types. +// Return: None. +// Throws: None. +//-- +CMICmdArgValString::CMICmdArgValString(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd, + const bool vbHandleQuotes, + const bool vbAcceptNumbers, + const bool vbHandleDirPaths) + : CMICmdArgValBaseTemplate(vrArgName, vbMandatory, vbHandleByCmd), + m_bHandleQuotedString(vbHandleQuotes), m_bAcceptNumbers(vbAcceptNumbers), + m_bHandleDirPaths(vbHandleDirPaths), m_bHandleAnything(false) {} + +//++ +// Details: CMICmdArgValString destructor. +// Type: Overridden. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgValString::~CMICmdArgValString() {} + +//++ +// Details: Parse the command's argument options string and try to extract the +// value *this +// argument is looking for. +// Type: Overridden. +// Args: vrwArgContext - (RW) The command's argument options string. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdArgValString::Validate(CMICmdArgContext &vrwArgContext) { + if (vrwArgContext.IsEmpty()) + return m_bMandatory ? MIstatus::failure : MIstatus::success; + + if (m_bHandleQuotedString) + return ValidateQuotedText(vrwArgContext); + + return ValidateSingleText(vrwArgContext); +} + +//++ +// Details: Parse the command's argument options string and try to extract only +// the next +// word delimited by the next space. +// Type: Method. +// Args: vrwArgContext - (RW) The command's argument options string. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdArgValString::ValidateSingleText(CMICmdArgContext &vrwArgContext) { + const CMIUtilString::VecString_t vecOptions(vrwArgContext.GetArgs()); + CMIUtilString::VecString_t::const_iterator it = vecOptions.begin(); + while (it != vecOptions.end()) { + const CMIUtilString &rArg(*it); + if (IsStringArg(rArg)) { + m_bFound = true; + + if (vrwArgContext.RemoveArg(rArg)) { + m_bValid = true; + m_argValue = rArg.StripSlashes(); + return MIstatus::success; + } else + return MIstatus::failure; + } + + // Next + ++it; + } + + return MIstatus::failure; +} + +//++ +// Details: Parse the command's argument options string and try to extract all +// the words +// between quotes then delimited by the next space. +// Type: Method. +// Args: vrwArgContext - (RW) The command's argument options string. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdArgValString::ValidateQuotedText(CMICmdArgContext &vrwArgContext) { + const CMIUtilString::VecString_t vecOptions(vrwArgContext.GetArgs()); + if (vecOptions.size() == 0) + return MIstatus::failure; + + const CMIUtilString &rArg(vecOptions[0]); + if (!IsStringArg(rArg)) + return MIstatus::failure; + + m_bFound = true; + + if (vrwArgContext.RemoveArg(rArg)) { + m_bValid = true; + const char cQuote = '"'; + m_argValue = rArg.Trim(cQuote).StripSlashes(); + return MIstatus::success; + } + + return MIstatus::failure; +} + +//++ +// Details: Examine the string and determine if it is a valid string type +// argument. +// Type: Method. +// Args: vrTxt - (R) Some text. +// Return: bool - True = yes valid arg, false = no. +// Throws: None. +//-- +bool CMICmdArgValString::IsStringArg(const CMIUtilString &vrTxt) const { + if (m_bHandleQuotedString) + return (IsStringArgQuotedText(vrTxt) || + IsStringArgQuotedTextEmbedded(vrTxt) || + IsStringArgQuotedQuotedTextEmbedded(vrTxt) || + IsStringArgSingleText( + vrTxt)); // Still test for this as could just be one word still + + return IsStringArgSingleText(vrTxt); +} + +//++ +// Details: Examine the string and determine if it is a valid string type +// argument or +// option value. If the string looks like a long option, short option, +// a thread +// group ID or just a number it is rejected as a string type value. +// There is an +// option to allow the string to accept a number as a string type. +// Type: Method. +// Args: vrTxt - (R) Some text. +// Return: bool - True = yes valid argument value, false = something else. +// Throws: None. +//-- +bool CMICmdArgValString::IsStringArgSingleText( + const CMIUtilString &vrTxt) const { + if (!m_bHandleDirPaths) { + // Look for directory file paths, if found reject + const bool bHavePosSlash = (vrTxt.find('/') != std::string::npos); + const bool bHaveBckSlash = (vrTxt.find('\\') != std::string::npos); + if (bHavePosSlash || bHaveBckSlash) + return false; + } + + // Look for --someLongOption, if found reject + if (0 == vrTxt.find("--")) + return false; + + // Look for -f type short options, if found reject + if ((0 == vrTxt.find('-')) && (vrTxt.length() == 2)) + return false; + + // Look for thread group i1 i2 i3...., if found reject + if ((vrTxt.find('i') == 0) && ::isdigit(vrTxt[1])) + return false; + + // Look for numbers, if found reject + if (!m_bAcceptNumbers && vrTxt.IsNumber()) + return false; + + return true; +} + +//++ +// Details: Examine the string and determine if it is a valid string type +// argument. +// Take into account quotes surrounding the text. Note this function +// falls +// through to IsStringArgSingleText() should the criteria match fail. +// Type: Method. +// Args: vrTxt - (R) Some text. +// Return: bool - True = yes valid arg, false = no. +// Throws: None. +//-- +bool CMICmdArgValString::IsStringArgQuotedText( + const CMIUtilString &vrTxt) const { + // Accept anything as string word + if (m_bHandleAnything) + return true; + + // CODETAG_QUOTEDTEXT_SIMILAR_CODE + const char cQuote = '"'; + const size_t nPos = vrTxt.find(cQuote); + if (nPos == std::string::npos) + return false; + + // Is one and only quote at end of the string + if (nPos == (vrTxt.length() - 1)) + return false; + + // Quote must be the first character in the string or be preceded by a space + // Also check for embedded string formating quote + const char cBckSlash = '\\'; + const char cSpace = ' '; + if ((nPos > 1) && (vrTxt[nPos - 1] == cBckSlash) && + (vrTxt[nPos - 2] != cSpace)) { + return false; + } + if ((nPos > 0) && (vrTxt[nPos - 1] != cSpace)) + return false; + + // Need to find the other quote + const size_t nPos2 = vrTxt.rfind(cQuote); + if (nPos2 == std::string::npos) + return false; + + // Make sure not same quote, need two quotes + if (nPos == nPos2) + return MIstatus::failure; + + return true; +} + +//++ +// Details: Examine the string and determine if it is a valid string type +// argument. +// Take into account quotes surrounding the text. Take into account +// string format +// embedded quotes surrounding the text i.e. "\\\"%5d\\\"". Note this +// function falls +// through to IsStringArgQuotedText() should the criteria match fail. +// Type: Method. +// Args: vrTxt - (R) Some text. +// Return: bool - True = yes valid arg, false = no. +// Throws: None. +//-- +bool CMICmdArgValString::IsStringArgQuotedTextEmbedded( + const CMIUtilString &vrTxt) const { + // CODETAG_QUOTEDTEXT_SIMILAR_CODE + const char cBckSlash = '\\'; + const size_t nPos = vrTxt.find(cBckSlash); + if (nPos == std::string::npos) + return false; + + // Slash must be the first character in the string or be preceded by a space + const char cSpace = ' '; + if ((nPos > 0) && (vrTxt[nPos - 1] != cSpace)) + return false; + + // Need to find the other matching slash + const size_t nPos2 = vrTxt.rfind(cBckSlash); + if (nPos2 == std::string::npos) + return false; + + // Make sure not same back slash, need two slashes + if (nPos == nPos2) + return MIstatus::failure; + + return false; +} + +//++ +// Details: Examine the string and determine if it is a valid string type +// argument. +// Take into account quotes surrounding the text. Take into account +// string format +// embedded quotes surrounding the text i.e. "\\\"%5d\\\"". Note this +// function falls +// through to IsStringArgQuotedTextEmbedded() should the criteria match +// fail. +// Type: Method. +// Args: vrTxt - (R) Some text. +// Return: bool - True = yes valid arg, false = no. +// Throws: None. +//-- +bool CMICmdArgValString::IsStringArgQuotedQuotedTextEmbedded( + const CMIUtilString &vrTxt) const { + const size_t nPos = vrTxt.find("\"\\\""); + if (nPos == std::string::npos) + return false; + + const size_t nPos2 = vrTxt.rfind("\\\"\""); + if (nPos2 == std::string::npos) + return false; + + const size_t nLen = vrTxt.length(); + return !((nLen > 5) && ((nPos + 2) == (nPos2 - 2))); +} diff --git a/src/MICmdArgValString.h b/src/MICmdArgValString.h new file mode 100644 index 00000000000..77041c2d0c9 --- /dev/null +++ b/src/MICmdArgValString.h @@ -0,0 +1,82 @@ +//===-- MICmdArgValString.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmdArgValBase.h" + +// Declarations: +class CMICmdArgContext; + +//++ +//============================================================================ +// Details: MI common code class. Command argument class. Arguments object +// needing specialization derived from the CMICmdArgValBase class. +// An argument knows what type of argument it is and how it is to +// interpret the options (context) string to find and validate a +// matching +// argument and so extract a value from it . +// Based on the Interpreter pattern. +//-- +class CMICmdArgValString : public CMICmdArgValBaseTemplate { + // Methods: +public: + /* ctor */ CMICmdArgValString(); + /* ctor */ CMICmdArgValString(const bool vbAnything); + /* ctor */ CMICmdArgValString(const bool vbHandleQuotes, + const bool vbAcceptNumbers, + const bool vbHandleDirPaths); + /* ctor */ CMICmdArgValString(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd, + const bool vbHandleQuotes = false, + const bool vbAcceptNumbers = false); + /* ctor */ CMICmdArgValString(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd, + const bool vbHandleQuotes, + const bool vbAcceptNumbers, + const bool vbHandleDirPaths); + // + bool IsStringArg(const CMIUtilString &vrTxt) const; + + // Overridden: +public: + // From CMICmdArgValBase + /* dtor */ ~CMICmdArgValString() override; + // From CMICmdArgSet::IArg + bool Validate(CMICmdArgContext &vrwArgContext) override; + + // Methods: +private: + bool ValidateSingleText(CMICmdArgContext &vrwArgContext); + bool ValidateQuotedText(CMICmdArgContext &vrwArgContext); + bool ValidateQuotedTextEmbedded(CMICmdArgContext &vrwArgContext); + bool ValidateQuotedQuotedTextEmbedded(CMICmdArgContext &vrwArgContext); + bool IsStringArgSingleText(const CMIUtilString &vrTxt) const; + bool IsStringArgQuotedText(const CMIUtilString &vrTxt) const; + bool IsStringArgQuotedTextEmbedded(const CMIUtilString &vrTxt) const; + bool IsStringArgQuotedQuotedTextEmbedded(const CMIUtilString &vrTxt) const; + + // Attribute: +private: + bool m_bHandleQuotedString; // True = Parse a string surrounded by quotes + // spaces are not delimiters, false = only text up + // to next + // delimiting space character + bool m_bAcceptNumbers; // True = Parse a string and accept as a number if + // number, false = numbers not recognised as string + // types + bool m_bHandleDirPaths; // True = Parse a string and accept directory file + // style string if present, false = directory file + // path not + // accepted + bool m_bHandleAnything; // True = Parse a string and accept anything if + // present, false = validate for criteria matches +}; diff --git a/src/MICmdArgValThreadGrp.cpp b/src/MICmdArgValThreadGrp.cpp new file mode 100644 index 00000000000..201d516525f --- /dev/null +++ b/src/MICmdArgValThreadGrp.cpp @@ -0,0 +1,141 @@ +//===-- MICmdArgValThreadGrp.cpp --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmdArgValThreadGrp.h" +#include "MICmdArgContext.h" + +//++ +// Details: CMICmdArgValThreadGrp constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgValThreadGrp::CMICmdArgValThreadGrp() : m_nThreadGrp(0) {} + +//++ +// Details: CMICmdArgValThreadGrp constructor. +// Type: Method. +// Args: vrArgName - (R) Argument's name to search by. +// vbMandatory - (R) True = Yes must be present, false = optional +// argument. +// vbHandleByCmd - (R) True = Command processes *this option, false = +// not handled. +// Return: None. +// Throws: None. +//-- +CMICmdArgValThreadGrp::CMICmdArgValThreadGrp(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd) + : CMICmdArgValBaseTemplate(vrArgName, vbMandatory, vbHandleByCmd), + m_nThreadGrp(0) {} + +//++ +// Details: CMICmdArgValThreadGrp destructor. +// Type: Overridden. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdArgValThreadGrp::~CMICmdArgValThreadGrp() {} + +//++ +// Details: Parse the command's argument options string and try to extract the +// value *this +// argument is looking for. +// Type: Overridden. +// Args: vwArgContext - (RW) The command's argument options string. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdArgValThreadGrp::Validate(CMICmdArgContext &vwArgContext) { + if (vwArgContext.IsEmpty()) + return m_bMandatory ? MIstatus::failure : MIstatus::success; + + if (vwArgContext.GetNumberArgsPresent() == 1) { + const CMIUtilString &rArg(vwArgContext.GetArgsLeftToParse()); + if (IsArgThreadGrp(rArg) && ExtractNumber(rArg)) { + m_bFound = true; + m_bValid = true; + m_argValue = GetNumber(); + vwArgContext.RemoveArg(rArg); + return MIstatus::success; + } else + return MIstatus::failure; + } + + // More than one option... + const CMIUtilString::VecString_t vecOptions(vwArgContext.GetArgs()); + CMIUtilString::VecString_t::const_iterator it = vecOptions.begin(); + while (it != vecOptions.end()) { + const CMIUtilString &rArg(*it); + if (IsArgThreadGrp(rArg) && ExtractNumber(rArg)) { + m_bFound = true; + + if (vwArgContext.RemoveArg(rArg)) { + m_bValid = true; + m_argValue = GetNumber(); + return MIstatus::success; + } else + return MIstatus::failure; + } + + // Next + ++it; + } + + return MIstatus::failure; +} + +//++ +// Details: Examine the string and determine if it is a valid string type +// argument. +// Type: Method. +// Args: vrTxt - (R) Some text. +// Return: bool - True = yes valid arg, false = no. +// Throws: None. +//-- +bool CMICmdArgValThreadGrp::IsArgThreadGrp(const CMIUtilString &vrTxt) const { + // Look for i1 i2 i3.... + const MIint nPos = vrTxt.find('i'); + if (nPos != 0) + return false; + + const CMIUtilString strNum = vrTxt.substr(1); + return strNum.IsNumber(); +} + +//++ +// Details: Extract the thread group number from the thread group argument. +// Type: Method. +// Args: vrTxt - (R) Some text. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdArgValThreadGrp::ExtractNumber(const CMIUtilString &vrTxt) { + const CMIUtilString strNum = vrTxt.substr(1); + MIint64 nNumber = 0; + bool bOk = strNum.ExtractNumber(nNumber); + if (bOk) { + m_nThreadGrp = static_cast(nNumber); + } + + return bOk; +} + +//++ +// Details: Retrieve the thread group ID found in the argument. +// Type: Method. +// Args: None. +// Return: MIuint - Thread group ID. +// Throws: None. +//-- +MIuint CMICmdArgValThreadGrp::GetNumber() const { return m_nThreadGrp; } diff --git a/src/MICmdArgValThreadGrp.h b/src/MICmdArgValThreadGrp.h new file mode 100644 index 00000000000..12d3a1e9024 --- /dev/null +++ b/src/MICmdArgValThreadGrp.h @@ -0,0 +1,53 @@ +//===-- MICmdArgValThreadGrp.h ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmdArgValBase.h" + +// Declarations: +class CMICmdArgContext; + +//++ +//============================================================================ +// Details: MI common code class. Command argument class. Arguments object +// needing specialization derived from the CMICmdArgValBase class. +// An argument knows what type of argument it is and how it is to +// interpret the options (context) string to find and validate a +// matching +// argument and so extract a value from it. Thread group looks like +// "i1" in the options text. +// Based on the Interpreter pattern. +//-- +class CMICmdArgValThreadGrp : public CMICmdArgValBaseTemplate { + // Methods: +public: + /* ctor */ CMICmdArgValThreadGrp(); + /* ctor */ CMICmdArgValThreadGrp(const CMIUtilString &vrArgName, + const bool vbMandatory, + const bool vbHandleByCmd); + // + bool IsArgThreadGrp(const CMIUtilString &vrTxt) const; + + // Overridden: +public: + // From CMICmdArgValBase + /* dtor */ ~CMICmdArgValThreadGrp() override; + // From CMICmdArgSet::IArg + bool Validate(CMICmdArgContext &vArgContext) override; + + // Methods: +private: + bool ExtractNumber(const CMIUtilString &vrTxt); + MIuint GetNumber() const; + + // Attributes: +private: + MIuint m_nThreadGrp; +}; diff --git a/src/MICmdBase.cpp b/src/MICmdBase.cpp new file mode 100644 index 00000000000..df36cfe8642 --- /dev/null +++ b/src/MICmdBase.cpp @@ -0,0 +1,329 @@ +//===-- MICmdBase.cpp -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmdBase.h" +#include "MICmdArgValConsume.h" +#include "MICmdArgValOptionLong.h" +#include "MICmnLLDBDebugSessionInfo.h" +#include "MICmnMIValueConst.h" + +//++ +// Details: CMICmdBase constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdBase::CMICmdBase() + : m_pSelfCreatorFn(nullptr), + m_rLLDBDebugSessionInfo(CMICmnLLDBDebugSessionInfo::Instance()), + m_bHasResultRecordExtra(false), m_constStrArgThreadGroup("thread-group"), + m_constStrArgThread("thread"), m_constStrArgFrame("frame"), + m_constStrArgConsume("--"), m_ThreadGrpArgMandatory(false), + m_ThreadArgMandatory(false), m_FrameArgMandatory(false) {} + +//++ +// Details: CMICmdBase destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdBase::~CMICmdBase() {} + +//++ +// Details: The invoker requires this function. +// Type: Overridden. +// Args: None. +// Return: SMICmdData & - *this command's present status/data/information. +// Throws: None. +//-- +const SMICmdData &CMICmdBase::GetCmdData() const { return m_cmdData; } + +//++ +// Details: The invoker requires this function. +// Type: Overridden. +// Args: None. +// Return: CMIUtilString & - *this command's current error description. +// Empty string indicates command status ok. +// Throws: None. +//-- +const CMIUtilString &CMICmdBase::GetErrorDescription() const { + return m_strCurrentErrDescription; +} + +//++ +// Details: The CMICmdFactory requires this function. Retrieve the command and +// argument +// options description string. +// Type: Overridden. +// Args: None. +// Return: CMIUtilString & - Command description. +// Throws: None. +//-- +const CMIUtilString &CMICmdBase::GetMiCmd() const { return m_strMiCmd; } + +//++ +// Details: Help parse the arguments that are common to all commands. +// Args: None. +// Return: None +// Throws: None. +//-- +void CMICmdBase::AddCommonArgs() { + m_setCmdArgs.Add(new CMICmdArgValOptionLong( + m_constStrArgThreadGroup, m_ThreadGrpArgMandatory, true, + CMICmdArgValListBase::eArgValType_ThreadGrp, 1)); + m_setCmdArgs.Add(new CMICmdArgValOptionLong( + m_constStrArgThread, m_ThreadArgMandatory, true, + CMICmdArgValListBase::eArgValType_Number, 1)); + m_setCmdArgs.Add( + new CMICmdArgValOptionLong(m_constStrArgFrame, m_FrameArgMandatory, true, + CMICmdArgValListBase::eArgValType_Number, 1)); + m_setCmdArgs.Add(new CMICmdArgValConsume(m_constStrArgConsume, false)); +} + +//++ +// Details: The invoker requires this function. A command must be given working +// data and +// provide data about its status or provide information to other +// objects. +// Type: Overridden. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMICmdBase::SetCmdData(const SMICmdData &vCmdData) { + m_cmdData = vCmdData; +} + +//++ +// Details: The command factory requires this function. The factory calls this +// function +// so it can obtain *this command's creation function. +// Type: Overridden. +// Args: None. +// Return: CMICmdFactory::CmdCreatorFnPtr - Function pointer. +// Throws: None. +//-- +CMICmdFactory::CmdCreatorFnPtr CMICmdBase::GetCmdCreatorFn() const { + return m_pSelfCreatorFn; +} + +//++ +// Details: If a command is an event type (has callbacks registered with +// SBListener) it +// needs to inform the Invoker that it has finished its work so that +// the +// Invoker can tidy up and call the commands Acknowledge function (yes +// the +// command itself could call the Acknowledge itself but not doing that +// way). +// Type: Overridden. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMICmdBase::CmdFinishedTellInvoker() const { + CMICmdInvoker::Instance().CmdExecuteFinished(const_cast(*this)); +} + +//++ +// Details: Returns the final version of the MI result record built up in the +// command's +// Acknowledge function. The one line text of MI result. +// Type: Overridden. +// Args: None. +// Return: CMIUtilString & - MI text version of the MI result record. +// Throws: None. +//-- +const CMIUtilString &CMICmdBase::GetMIResultRecord() const { + return m_miResultRecord.GetString(); +} + +//++ +// Details: Retrieve from the command additional MI result to its 1 line +// response. +// Because of using LLDB additional 'fake'/hack output is sometimes +// required to +// help the driver client operate i.e. Eclipse. +// Type: Overridden. +// Args: None. +// Return: CMIUtilString & - MI text version of the MI result record. +// Throws: None. +//-- +const CMIUtilString &CMICmdBase::GetMIResultRecordExtra() const { + return m_miResultRecordExtra; +} + +//++ +// Details: Hss *this command got additional MI result to its 1 line response. +// Because of using LLDB additional 'fake'/hack output is sometimes +// required to +// help the driver client operate i.e. Eclipse. +// Type: Overridden. +// Args: None. +// Return: bool - True = Yes have additional MI output, false = no nothing +// extra. +// Throws: None. +//-- +bool CMICmdBase::HasMIResultRecordExtra() const { + return m_bHasResultRecordExtra; +} + +//++ +// Details: Short cut function to enter error information into the command's +// metadata +// object and set the command's error status. +// Type: Method. +// Args: rErrMsg - (R) Status description. +// Return: None. +// Throws: None. +//-- +void CMICmdBase::SetError(const CMIUtilString &rErrMsg) { + m_cmdData.bCmdValid = false; + m_cmdData.strErrorDescription = rErrMsg; + m_cmdData.bCmdExecutedSuccessfully = false; + + const CMICmnMIValueResult valueResult("msg", CMICmnMIValueConst(rErrMsg)); + const CMICmnMIResultRecord miResultRecord( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + valueResult); + m_miResultRecord = miResultRecord; + m_cmdData.strMiCmdResultRecord = miResultRecord.GetString(); +} + +//++ +// Details: Short cut function to check MI command's execute status and +// set an error in case of failure. +// Type: Method. +// Args: error - (R) Error description object. +// successHandler - (R) function describing actions to execute +// in case of success state of passed SBError object. +// errorHandler - (R) function describing actions to execute +// in case of fail status of passed SBError object. +// Return: bool. +// Throws: None. +//-- +bool CMICmdBase::HandleSBError(const lldb::SBError &error, + const std::function &successHandler, + const std::function &errorHandler) { + if (error.Success()) + return successHandler(); + + SetError(error.GetCString()); + errorHandler(); + return MIstatus::failure; +} + +//++ +// Details: Short cut function to check MI command's execute status and +// call specified handler function for success case. +// Type: Method. +// Args: error - (R) Error description object. +// successHandler - (R) function describing actions to execute +// in case of success state of passed SBError object. +// Return: bool. +// Throws: None. +//-- +bool CMICmdBase::HandleSBErrorWithSuccess( + const lldb::SBError &error, + const std::function &successHandler) { + return HandleSBError(error, successHandler); +} + +//++ +// Details: Short cut function to check MI command's execute status and +// call specified handler function for error case. +// Type: Method. +// Args: error - (R) Error description object. +// errorHandler - (R) function describing actions to execute +// in case of fail status of passed SBError object. +// Return: bool. +// Throws: None. +//-- +bool CMICmdBase::HandleSBErrorWithFailure( + const lldb::SBError &error, + const std::function &errorHandler) { + return HandleSBError(error, [] { return MIstatus::success; }, errorHandler); +} + +//++ +// Details: Ask a command to provide its unique identifier. +// Type: Method. +// Args: A unique identifier for this command class. +// Return: None. +// Throws: None. +//-- +MIuint CMICmdBase::GetGUID() { + MIuint64 vptr = reinterpret_cast(this); + MIuint id = (vptr)&0xFFFFFFFF; + id ^= (vptr >> 32) & 0xFFFFFFFF; + + return id; +} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdBase::ParseArgs() { + // Do nothing - override to implement + + return MIstatus::success; +} + +//++ +// Details: Having previously given CMICmdArgSet m_setCmdArgs all the argument +// or option +// definitions for the command to handle proceed to parse and validate +// the +// command's options text for those arguments and extract the values +// for each if +// any. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdBase::ParseValidateCmdOptions() { + CMICmdArgContext argCntxt(m_cmdData.strMiCmdOption); + if (m_setCmdArgs.Validate(m_cmdData.strMiCmd, argCntxt)) + return MIstatus::success; + + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_ARGS), + m_cmdData.strMiCmd.c_str(), + m_setCmdArgs.GetErrorDescription().c_str())); + + return MIstatus::failure; +} + +//++ +// Details: If the MI Driver is not operating via a client i.e. Eclipse but say +// operating +// on a executable passed in as a argument to the drive then what +// should the driver +// do on a command failing? Either continue operating or exit the +// application. +// Override this function where a command failure cannot allow the +// driver to +// continue operating. +// Type: Overrideable. +// Args: None. +// Return: bool - True = Fatal if command fails, false = can continue if +// command fails. +// Throws: None. +//-- +bool CMICmdBase::GetExitAppOnCommandFailure() const { return false; } diff --git a/src/MICmdBase.h b/src/MICmdBase.h new file mode 100644 index 00000000000..4e32ed6a526 --- /dev/null +++ b/src/MICmdBase.h @@ -0,0 +1,193 @@ +//===-- MICmdBase.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include + +#include "lldb/API/SBError.h" + +#include "MICmdArgSet.h" +#include "MICmdData.h" +#include "MICmdFactory.h" +#include "MICmdInvoker.h" +#include "MICmnBase.h" +#include "MICmnMIResultRecord.h" +#include "MICmnResources.h" +#include "MIUtilString.h" + +// Declarations: +class CMICmnLLDBDebugSessionInfo; + +//++ +//============================================================================ +// Details: MI command base class. MI commands derive from this base class. +// The Command Factory creates command objects and passes them to the +// Command Invoker. The Invoker takes ownership of any commands created +// which means it is the only object to delete them when a command is +// finished working. Commands do not delete themselves. +// There are two types of command implicitly defined by the state of +// the m_bWaitForEventFromSBDebugger flag. There is the event type +// command which registers (command fn) callbacks with the SBListener +// does some work then wakes up again when called back, does more work +// perhaps, ends, then the Invoker calls the command's Acknowledge +// function. The other type of command is one that just does some work, +// ends, then the Invoker calls the command's Acknowledge function. No +// events set up. +// A command's Execute(), Acknowledge() and event callback functions +// are +// carried out in the main thread. +// A command may use the argument derived object classes +// (CMICmdArgValBase) +// to factor handling and parsing of different types of arguments +// presented to a command. A command will produce an error should it +// be presented with arguments or options it does not understand. +//-- +class CMICmdBase : public CMICmnBase, + public CMICmdInvoker::ICmd, + public CMICmdFactory::ICmd { + // Methods: +public: + CMICmdBase(); + + // Overridden: + // From CMICmdInvoker::ICmd + const SMICmdData &GetCmdData() const override; + const CMIUtilString &GetErrorDescription() const override; + void SetCmdData(const SMICmdData &vCmdData) override; + void CmdFinishedTellInvoker() const override; + const CMIUtilString &GetMIResultRecord() const override; + const CMIUtilString &GetMIResultRecordExtra() const override; + bool HasMIResultRecordExtra() const override; + bool ParseArgs() override; + // From CMICmdFactory::ICmd + const CMIUtilString &GetMiCmd() const override; + CMICmdFactory::CmdCreatorFnPtr GetCmdCreatorFn() const override; + + virtual MIuint GetGUID(); + void AddCommonArgs(); + + // Overrideable: + ~CMICmdBase() override; + virtual bool GetExitAppOnCommandFailure() const; + + // Methods: +protected: + void SetError(const CMIUtilString &rErrMsg); + bool HandleSBError(const lldb::SBError &error, + const std::function &successHandler = + [] { return MIstatus::success; }, + const std::function &errorHandler = [] {}); + bool HandleSBErrorWithSuccess(const lldb::SBError &error, + const std::function &successHandler); + bool HandleSBErrorWithFailure(const lldb::SBError &error, + const std::function &errorHandler); + template T *GetOption(const CMIUtilString &vStrOptionName); + bool ParseValidateCmdOptions(); + + // Attributes: + CMICmdFactory::CmdCreatorFnPtr m_pSelfCreatorFn; + CMIUtilString m_strCurrentErrDescription; // Reason for Execute or Acknowledge + // function failure + SMICmdData m_cmdData; // Holds information/status of *this command. Used by + // other MI code to report or determine state of a + // command. + bool m_bWaitForEventFromSBDebugger; // True = yes event type command wait, + // false = command calls Acknowledge() + // straight after Execute() + // no waiting + CMIUtilString + m_strMiCmd; // The MI text identifying *this command i.e. 'break-insert' + CMICmnMIResultRecord m_miResultRecord; // This is completed in the + // Acknowledge() function and returned + // to the Command Invoker to proceed + // stdout output. Each command forms 1 response to its input. + CMIUtilString m_miResultRecordExtra; // This is completed in the Acknowledge() + // function and returned to the Command + // Invoker to proceed + // stdout output. Hack command produce more response text to help the client + // because of using LLDB + CMICmnLLDBDebugSessionInfo &m_rLLDBDebugSessionInfo; // Access to command + // sharing information or + // data across any and + // all command based + // derived classes. + bool m_bHasResultRecordExtra; // True = Yes command produced additional MI + // output to its 1 line response, false = no + // extra MI output + // formed. + CMICmdArgSet m_setCmdArgs; // The list of arguments *this command needs to + // parse from the options string to carry out work. + const CMIUtilString m_constStrArgThreadGroup; + const CMIUtilString m_constStrArgThread; + const CMIUtilString m_constStrArgFrame; + const CMIUtilString m_constStrArgConsume; + + // These 3 members can be used by the derived classes to make any of + // "thread", "frame" or "thread-group" mandatory. + bool m_ThreadGrpArgMandatory; + bool m_ThreadArgMandatory; + bool m_FrameArgMandatory; +}; + +//++ +// Details: Retrieve the command argument or option object pointer so that it +// can be +// examined. If the option found and valid get the value (number, +// string or list +// - see CMICmdArgValBase class) from it to use with the command's +// decision +// making. If the argument is not found the command's error description +// is set +// describing the error condition. +// Type: Template method. +// Args: vStrOptionName - (R) The text name of the argument or option to +// search for in +// the list of the command's possible arguments +// or options. +// Return: T * - CMICmdArgValBase derived object. +// - nullptr = function has failed, unable to retrieve the +// option/arg object. +// Throws: None. +//-- +template +T *CMICmdBase::GetOption(const CMIUtilString &vStrOptionName) { + CMICmdArgValBase *pPtrBase = nullptr; + if (!m_setCmdArgs.GetArg(vStrOptionName, pPtrBase)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND), + m_cmdData.strMiCmd.c_str(), + vStrOptionName.c_str())); + return nullptr; + } + + return static_cast(pPtrBase); +} + +//++ +// Details: Retrieve the command argument or option object pointer using +// template function +// CMICmdBase::GetOption(). Should the argument (by name) not be found +// the +// command will exit with a failure (set in GetOption()). +// Type: Preprocessor macro. +// Args: a - (R) The actual variable's name. +// b - (R) The type of variable (appended to CMICmdArgVal i.e. +// CMICmdArgValString). +// c - (R) The text name of the argument or option to search for in +// the list of +// the command's possible arguments or options. +// Return: T * - CMICmdArgValBase derived object. +// - nullptr = function has failed, unable to retrieve the +// option/arg object. +// Throws: None. +//-- +#define CMICMDBASE_GETOPTION(a, b, c) \ + CMICmdArgVal##b *a = CMICmdBase::GetOption(c); \ + if (a == nullptr) \ + return MIstatus::failure; +// This comment is to stop compile warning for #define diff --git a/src/MICmdCmd.cpp b/src/MICmdCmd.cpp new file mode 100644 index 00000000000..081cff6fa51 --- /dev/null +++ b/src/MICmdCmd.cpp @@ -0,0 +1,158 @@ +//===-- MICmdCmd.cpp --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdEnablePrettyPrinting implementation. +// CMICmdCmdSource implementation. +// + +// In-house headers: +#include "MICmdCmd.h" + +//++ +// Details: CMICmdCmdEnablePrettyPrinting constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdEnablePrettyPrinting::CMICmdCmdEnablePrettyPrinting() { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "enable-pretty-printing"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdEnablePrettyPrinting::CreateSelf; +} + +//++ +// Details: CMICmdCmdEnablePrettyPrinting destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdEnablePrettyPrinting::~CMICmdCmdEnablePrettyPrinting() {} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdEnablePrettyPrinting::Execute() { + // Do nothing + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdEnablePrettyPrinting::Acknowledge() { + const CMICmnMIValueConst miValueConst("0"); + const CMICmnMIValueResult miValueResult("supported", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdEnablePrettyPrinting::CreateSelf() { + return new CMICmdCmdEnablePrettyPrinting(); +} + + +//++ +// Details: CMICmdCmdSource constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdSource::CMICmdCmdSource() { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "source"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdSource::CreateSelf; +} + +//++ +// Details: CMICmdCmdSource destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdSource::~CMICmdCmdSource() {} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdSource::Execute() { + // Do nothing + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdSource::Acknowledge() { + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdSource::CreateSelf() { return new CMICmdCmdSource(); } diff --git a/src/MICmdCmd.h b/src/MICmdCmd.h new file mode 100644 index 00000000000..aeaaa4b01db --- /dev/null +++ b/src/MICmdCmd.h @@ -0,0 +1,90 @@ +//===-- MICmdCmd.h ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdEnablePrettyPrinting interface. +// CMICmdCmdSource interface. +// +// To implement new MI commands derive a new command class from the +// command base +// class. To enable the new command for interpretation add the new +// command class +// to the command factory. The files of relevance are: +// MICmdCommands.cpp +// MICmdBase.h / .cpp +// MICmdCmd.h / .cpp +// For an introduction to adding a new command see +// CMICmdCmdSupportInfoMiCmdQuery +// command class as an example. + +/* +MI commands implemented are: + See MICmdCommands.cpp +*/ + +#pragma once + +// Third party headers: +#include "lldb/API/SBBreakpoint.h" +#include "lldb/API/SBCommandReturnObject.h" +#include + +// In-house headers: +#include "MICmdBase.h" +#include "MICmnMIValueList.h" +#include "MICmnMIValueTuple.h" + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "enable-pretty-printing". +// Enables Python base pretty printing. +// Ref: +// http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Variable-Objects.html +//-- +class CMICmdCmdEnablePrettyPrinting : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdEnablePrettyPrinting(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdEnablePrettyPrinting() override; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "source". +//-- +class CMICmdCmdSource : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdSource(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdSource() override; +}; diff --git a/src/MICmdCmdBreak.cpp b/src/MICmdCmdBreak.cpp new file mode 100644 index 00000000000..1cd0bacf51d --- /dev/null +++ b/src/MICmdCmdBreak.cpp @@ -0,0 +1,1024 @@ +//===-- MICmdCmdBreak.cpp ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdBreakInsert implementation. +// CMICmdCmdBreakDelete implementation. +// CMICmdCmdBreakDisable implementation. +// CMICmdCmdBreakEnable implementation. +// CMICmdCmdBreakAfter implementation. +// CMICmdCmdBreakCondition implementation. + +// Third Party Headers: +#include "lldb/API/SBBreakpointLocation.h" + +// In-house headers: +#include "MICmdArgValFile.h" +#include "MICmdArgValListOfN.h" +#include "MICmdArgValNumber.h" +#include "MICmdArgValOptionLong.h" +#include "MICmdArgValOptionShort.h" +#include "MICmdArgValString.h" +#include "MICmdArgValThreadGrp.h" +#include "MICmdCmdBreak.h" +#include "MICmnLLDBDebugSessionInfo.h" +#include "MICmnLLDBDebugger.h" +#include "MICmnMIOutOfBandRecord.h" +#include "MICmnMIResultRecord.h" +#include "MICmnMIValueConst.h" +#include "MICmnStreamStdout.h" + +//++ +// Details: CMICmdCmdBreakInsert constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdBreakInsert::CMICmdCmdBreakInsert() + : m_bBrkPtIsTemp(false), m_bBrkPtIsPending(false), m_nBrkPtIgnoreCount(0), + m_bBrkPtEnabled(false), m_bBrkPtCondition(false), m_bBrkPtThreadId(false), + m_nBrkPtThreadId(0), m_constStrArgNamedTempBrkPt("t"), + m_constStrArgNamedHWBrkPt("h"), m_constStrArgNamedPendinfBrkPt("f"), + m_constStrArgNamedDisableBrkPt("d"), m_constStrArgNamedTracePt("a"), + m_constStrArgNamedConditionalBrkPt("c"), m_constStrArgNamedInoreCnt("i"), + m_constStrArgNamedRestrictBrkPtToThreadId("p"), + m_constStrArgNamedLocation("location") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "break-insert"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdBreakInsert::CreateSelf; +} + +//++ +// Details: CMICmdCmdBreakInsert destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdBreakInsert::~CMICmdCmdBreakInsert() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdBreakInsert::ParseArgs() { + m_setCmdArgs.Add( + new CMICmdArgValOptionShort(m_constStrArgNamedTempBrkPt, false, true)); + // Not implemented m_setCmdArgs.Add(new CMICmdArgValOptionShort( + // m_constStrArgNamedHWBrkPt, false, false)); + m_setCmdArgs.Add(new CMICmdArgValOptionShort( + m_constStrArgNamedPendinfBrkPt, false, true, + CMICmdArgValListBase::eArgValType_StringQuotedNumberPath, 1)); + m_setCmdArgs.Add(new CMICmdArgValOptionShort(m_constStrArgNamedDisableBrkPt, + false, false)); + // Not implemented m_setCmdArgs.Add(new CMICmdArgValOptionShort( + // m_constStrArgNamedTracePt, false, false)); + m_setCmdArgs.Add(new CMICmdArgValOptionShort( + m_constStrArgNamedConditionalBrkPt, false, true, + CMICmdArgValListBase::eArgValType_StringQuoted, 1)); + m_setCmdArgs.Add( + new CMICmdArgValOptionShort(m_constStrArgNamedInoreCnt, false, true, + CMICmdArgValListBase::eArgValType_Number, 1)); + m_setCmdArgs.Add(new CMICmdArgValOptionShort( + m_constStrArgNamedRestrictBrkPtToThreadId, false, true, + CMICmdArgValListBase::eArgValType_Number, 1)); + m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgNamedLocation, false, + true, false, false, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Helper function for CMICmdCmdBreakInsert::Execute(). +// +// Given a string, return the position of the ':' separator in 'file:func' +// or 'file:line', if any. If not found, return npos. For example, return +// 5 for 'foo.c:std::string'. +//-- +static size_t findFileSeparatorPos(const std::string &x) { + // Full paths in windows can have ':' after a drive letter, so we + // search backwards, taking care to skip C++ namespace tokens '::'. + size_t n = x.rfind(':'); + while (n != std::string::npos && n > 1 && x[n - 1] == ':') { + n = x.rfind(':', n - 2); + } + return n; +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdBreakInsert::Execute() { + CMICMDBASE_GETOPTION(pArgTempBrkPt, OptionShort, m_constStrArgNamedTempBrkPt); + CMICMDBASE_GETOPTION(pArgThreadGroup, OptionLong, m_constStrArgThreadGroup); + CMICMDBASE_GETOPTION(pArgLocation, String, m_constStrArgNamedLocation); + CMICMDBASE_GETOPTION(pArgIgnoreCnt, OptionShort, m_constStrArgNamedInoreCnt); + CMICMDBASE_GETOPTION(pArgPendingBrkPt, OptionShort, + m_constStrArgNamedPendinfBrkPt); + CMICMDBASE_GETOPTION(pArgDisableBrkPt, OptionShort, + m_constStrArgNamedDisableBrkPt); + CMICMDBASE_GETOPTION(pArgConditionalBrkPt, OptionShort, + m_constStrArgNamedConditionalBrkPt); + CMICMDBASE_GETOPTION(pArgRestrictBrkPtToThreadId, OptionShort, + m_constStrArgNamedRestrictBrkPtToThreadId); + + // Ask LLDB for the target to check if we have valid or dummy one. + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBTarget sbTarget = rSessionInfo.GetTarget(); + + m_bBrkPtEnabled = !pArgDisableBrkPt->GetFound(); + m_bBrkPtIsTemp = pArgTempBrkPt->GetFound(); + m_bHaveArgOptionThreadGrp = pArgThreadGroup->GetFound(); + if (m_bHaveArgOptionThreadGrp) { + MIuint nThreadGrp = 0; + pArgThreadGroup->GetExpectedOption( + nThreadGrp); + m_strArgOptionThreadGrp = CMIUtilString::Format("i%d", nThreadGrp); + } + + if (sbTarget == rSessionInfo.GetDebugger().GetDummyTarget()) + m_bBrkPtIsPending = true; + else { + m_bBrkPtIsPending = pArgPendingBrkPt->GetFound(); + if (!m_bBrkPtIsPending) { + CMIUtilString pending; + if (m_rLLDBDebugSessionInfo.SharedDataRetrieve("breakpoint.pending", pending)) { + m_bBrkPtIsPending = pending == "on"; + } + } + } + + if (pArgLocation->GetFound()) + m_brkName = pArgLocation->GetValue(); + else if (m_bBrkPtIsPending) { + pArgPendingBrkPt->GetExpectedOption( + m_brkName); + } + if (pArgIgnoreCnt->GetFound()) { + pArgIgnoreCnt->GetExpectedOption( + m_nBrkPtIgnoreCount); + } + m_bBrkPtCondition = pArgConditionalBrkPt->GetFound(); + if (m_bBrkPtCondition) { + pArgConditionalBrkPt->GetExpectedOption( + m_brkPtCondition); + } + m_bBrkPtThreadId = pArgRestrictBrkPtToThreadId->GetFound(); + if (m_bBrkPtCondition) { + pArgRestrictBrkPtToThreadId->GetExpectedOption( + m_nBrkPtThreadId); + } + + // Determine if break on a file line or at a function + BreakPoint_e eBrkPtType = eBreakPoint_NotDefineYet; + CMIUtilString fileName; + MIuint nFileLine = 0; + CMIUtilString strFileFn; + CMIUtilString rStrLineOrFn; + // Is the string in the form 'file:func' or 'file:line'? + // If so, find the position of the ':' separator. + const size_t nPosColon = findFileSeparatorPos(m_brkName); + if (nPosColon != std::string::npos) { + // Extract file name and line number from it + fileName = m_brkName.substr(0, nPosColon); + rStrLineOrFn = + m_brkName.substr(nPosColon + 1, m_brkName.size() - nPosColon - 1); + + if (rStrLineOrFn.empty()) + eBrkPtType = eBreakPoint_ByName; + else { + MIint64 nValue = 0; + if (rStrLineOrFn.ExtractNumber(nValue)) { + nFileLine = static_cast(nValue); + eBrkPtType = eBreakPoint_ByFileLine; + } else { + strFileFn = rStrLineOrFn; + eBrkPtType = eBreakPoint_ByFileFn; + } + } + } + + // Determine if break defined as an address + lldb::addr_t nAddress = 0; + if (eBrkPtType == eBreakPoint_NotDefineYet) { + MIint64 nValue = 0; + if (m_brkName.ExtractNumber(nValue)) { + nAddress = static_cast(nValue); + eBrkPtType = eBreakPoint_ByAddress; + } + } + + // Break defined as an function + if (eBrkPtType == eBreakPoint_NotDefineYet) { + eBrkPtType = eBreakPoint_ByName; + } + + // Ask LLDB to create a breakpoint + bool bOk = MIstatus::success; + switch (eBrkPtType) { + case eBreakPoint_ByAddress: + m_brkPt = sbTarget.BreakpointCreateByAddress(nAddress); + break; + case eBreakPoint_ByFileFn: { + lldb::SBFileSpecList module; // search in all modules + lldb::SBFileSpecList compUnit; + compUnit.Append(lldb::SBFileSpec(fileName.c_str())); + m_brkPt = + sbTarget.BreakpointCreateByName(strFileFn.c_str(), module, compUnit); + break; + } + case eBreakPoint_ByFileLine: + m_brkPt = sbTarget.BreakpointCreateByLocation(fileName.c_str(), nFileLine); + break; + case eBreakPoint_ByName: + m_brkPt = sbTarget.BreakpointCreateByName(m_brkName.c_str(), nullptr); + break; + case eBreakPoint_count: + case eBreakPoint_NotDefineYet: + case eBreakPoint_Invalid: + bOk = MIstatus::failure; + break; + } + + if (bOk) { + if (!m_bBrkPtIsPending && (m_brkPt.GetNumLocations() == 0)) { + sbTarget.BreakpointDelete(m_brkPt.GetID()); + SetError( + CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_BRKPT_LOCATION_NOT_FOUND), + m_cmdData.strMiCmd.c_str(), m_brkName.c_str())); + return MIstatus::failure; + } + + m_brkPt.SetEnabled(m_bBrkPtEnabled); + m_brkPt.SetIgnoreCount(m_nBrkPtIgnoreCount); + if (m_bBrkPtCondition) + m_brkPt.SetCondition(m_brkPtCondition.c_str()); + if (m_bBrkPtThreadId) + m_brkPt.SetThreadID(m_nBrkPtThreadId); + } + + // CODETAG_LLDB_BREAKPOINT_CREATION + // This is in the main thread + // Record break point information to be by LLDB event handler function + CMICmnLLDBDebugSessionInfo::SBrkPtInfo sBrkPtInfo; + if (!rSessionInfo.GetBrkPtInfo(m_brkPt, sBrkPtInfo)) + return MIstatus::failure; + sBrkPtInfo.m_id = m_brkPt.GetID(); + sBrkPtInfo.m_bDisp = m_bBrkPtIsTemp; + sBrkPtInfo.m_bEnabled = m_bBrkPtEnabled; + sBrkPtInfo.m_bHaveArgOptionThreadGrp = m_bHaveArgOptionThreadGrp; + sBrkPtInfo.m_strOptThrdGrp = m_strArgOptionThreadGrp; + sBrkPtInfo.m_nTimes = m_brkPt.GetHitCount(); + sBrkPtInfo.m_strOrigLoc = m_brkName; + sBrkPtInfo.m_nIgnore = m_nBrkPtIgnoreCount; + sBrkPtInfo.m_bPending = m_bBrkPtIsPending; + sBrkPtInfo.m_bCondition = m_bBrkPtCondition; + sBrkPtInfo.m_strCondition = m_brkPtCondition; + sBrkPtInfo.m_bBrkPtThreadId = m_bBrkPtThreadId; + sBrkPtInfo.m_nBrkPtThreadId = m_nBrkPtThreadId; + + bOk = bOk && rSessionInfo.RecordBrkPtInfo(m_brkPt.GetID(), sBrkPtInfo); + if (!bOk) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_BRKPT_INVALID), + m_cmdData.strMiCmd.c_str(), + m_brkName.c_str())); + return MIstatus::failure; + } + + // CODETAG_LLDB_BRKPT_ID_MAX + if (m_brkPt.GetID() > (lldb::break_id_t)rSessionInfo.m_nBrkPointCntMax) { + SetError(CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_BRKPT_CNT_EXCEEDED), m_cmdData.strMiCmd.c_str(), + rSessionInfo.m_nBrkPointCntMax, m_brkName.c_str())); + return MIstatus::failure; + } + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdBreakInsert::Acknowledge() { + // Get breakpoint information + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + CMICmnLLDBDebugSessionInfo::SBrkPtInfo sBrkPtInfo; + if (!rSessionInfo.RecordBrkPtInfoGet(m_brkPt.GetID(), sBrkPtInfo)) + return MIstatus::failure; + + // MI print + // "^done,bkpt={number=\"%d\",type=\"breakpoint\",disp=\"%s\",enabled=\"%c\",addr=\"0x%016" + // PRIx64 + // "\",func=\"%s\",file=\"%s\",fullname=\"%s/%s\",line=\"%d\",thread-groups=[\"%s\"],times=\"%d\",original-location=\"%s\"}" + CMICmnMIValueTuple miValueTuple; + if (!rSessionInfo.MIResponseFormBrkPtInfo(sBrkPtInfo, miValueTuple)) + return MIstatus::failure; + + const CMICmnMIValueResult miValueResultD("bkpt", miValueTuple); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResultD); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdBreakInsert::CreateSelf() { + return new CMICmdCmdBreakInsert(); +} + + +//++ +// Details: CMICmdCmdBreakDelete constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdBreakDelete::CMICmdCmdBreakDelete() + : m_constStrArgNamedBrkPt("breakpoint") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "break-delete"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdBreakDelete::CreateSelf; +} + +//++ +// Details: CMICmdCmdBreakDelete destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdBreakDelete::~CMICmdCmdBreakDelete() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdBreakDelete::ParseArgs() { + m_setCmdArgs.Add( + new CMICmdArgValListOfN(m_constStrArgNamedBrkPt, true, true, + CMICmdArgValListBase::eArgValType_Number)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdBreakDelete::Execute() { + CMICMDBASE_GETOPTION(pArgBrkPt, ListOfN, m_constStrArgNamedBrkPt); + + // ATM we only handle one break point ID + MIuint64 nBrk = UINT64_MAX; + if (!pArgBrkPt->GetExpectedOption(nBrk)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_BRKPT_INVALID), + m_cmdData.strMiCmd.c_str(), + m_constStrArgNamedBrkPt.c_str())); + return MIstatus::failure; + } + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + const bool bBrkPt = rSessionInfo.GetTarget().BreakpointDelete( + static_cast(nBrk)); + if (!bBrkPt) { + const CMIUtilString strBrkNum(CMIUtilString::Format("%d", nBrk)); + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_BRKPT_INVALID), + m_cmdData.strMiCmd.c_str(), + strBrkNum.c_str())); + return MIstatus::failure; + } + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdBreakDelete::Acknowledge() { + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdBreakDelete::CreateSelf() { + return new CMICmdCmdBreakDelete(); +} + + +//++ +// Details: CMICmdCmdBreakDisable constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdBreakDisable::CMICmdCmdBreakDisable() + : m_constStrArgNamedBrkPt("breakpoint"), m_bBrkPtDisabledOk(false), + m_nBrkPtId(0) { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "break-disable"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdBreakDisable::CreateSelf; +} + +//++ +// Details: CMICmdCmdBreakDisable destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdBreakDisable::~CMICmdCmdBreakDisable() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdBreakDisable::ParseArgs() { + m_setCmdArgs.Add( + new CMICmdArgValListOfN(m_constStrArgNamedBrkPt, true, true, + CMICmdArgValListBase::eArgValType_Number)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdBreakDisable::Execute() { + CMICMDBASE_GETOPTION(pArgBrkPt, ListOfN, m_constStrArgNamedBrkPt); + + // ATM we only handle one break point ID + MIuint64 nBrk = UINT64_MAX; + if (!pArgBrkPt->GetExpectedOption(nBrk)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_BRKPT_INVALID), + m_cmdData.strMiCmd.c_str(), + m_constStrArgNamedBrkPt.c_str())); + return MIstatus::failure; + } + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBBreakpoint brkPt = rSessionInfo.GetTarget().FindBreakpointByID( + static_cast(nBrk)); + if (brkPt.IsValid()) { + m_bBrkPtDisabledOk = true; + brkPt.SetEnabled(false); + m_nBrkPtId = nBrk; + } + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdBreakDisable::Acknowledge() { + if (m_bBrkPtDisabledOk) { + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + + const CMIUtilString strBrkPtId(CMIUtilString::Format("%d", m_nBrkPtId)); + const CMICmnMIValueConst miValueConst(CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_BRKPT_INVALID), strBrkPtId.c_str())); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdBreakDisable::CreateSelf() { + return new CMICmdCmdBreakDisable(); +} + + +//++ +// Details: CMICmdCmdBreakEnable constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdBreakEnable::CMICmdCmdBreakEnable() + : m_constStrArgNamedBrkPt("breakpoint"), m_bBrkPtEnabledOk(false), + m_nBrkPtId(0) { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "break-enable"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdBreakEnable::CreateSelf; +} + +//++ +// Details: CMICmdCmdBreakEnable destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdBreakEnable::~CMICmdCmdBreakEnable() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdBreakEnable::ParseArgs() { + m_setCmdArgs.Add( + new CMICmdArgValListOfN(m_constStrArgNamedBrkPt, true, true, + CMICmdArgValListBase::eArgValType_Number)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdBreakEnable::Execute() { + CMICMDBASE_GETOPTION(pArgBrkPt, ListOfN, m_constStrArgNamedBrkPt); + + // ATM we only handle one break point ID + MIuint64 nBrk = UINT64_MAX; + if (!pArgBrkPt->GetExpectedOption(nBrk)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_BRKPT_INVALID), + m_cmdData.strMiCmd.c_str(), + m_constStrArgNamedBrkPt.c_str())); + return MIstatus::failure; + } + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBBreakpoint brkPt = rSessionInfo.GetTarget().FindBreakpointByID( + static_cast(nBrk)); + if (brkPt.IsValid()) { + m_bBrkPtEnabledOk = true; + brkPt.SetEnabled(true); + m_nBrkPtId = nBrk; + } + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdBreakEnable::Acknowledge() { + if (m_bBrkPtEnabledOk) { + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + + const CMIUtilString strBrkPtId(CMIUtilString::Format("%d", m_nBrkPtId)); + const CMICmnMIValueConst miValueConst(CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_BRKPT_INVALID), strBrkPtId.c_str())); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdBreakEnable::CreateSelf() { + return new CMICmdCmdBreakEnable(); +} + + +//++ +// Details: CMICmdCmdBreakAfter constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdBreakAfter::CMICmdCmdBreakAfter() + : m_constStrArgNamedNumber("number"), m_constStrArgNamedCount("count"), + m_nBrkPtId(0), m_nBrkPtCount(0) { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "break-after"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdBreakAfter::CreateSelf; +} + +//++ +// Details: CMICmdCmdBreakAfter destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdBreakAfter::~CMICmdCmdBreakAfter() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdBreakAfter::ParseArgs() { + m_setCmdArgs.Add( + new CMICmdArgValNumber(m_constStrArgNamedNumber, true, true)); + m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgNamedCount, true, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdBreakAfter::Execute() { + CMICMDBASE_GETOPTION(pArgNumber, Number, m_constStrArgNamedNumber); + CMICMDBASE_GETOPTION(pArgCount, Number, m_constStrArgNamedCount); + + m_nBrkPtId = pArgNumber->GetValue(); + m_nBrkPtCount = pArgCount->GetValue(); + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBBreakpoint brkPt = rSessionInfo.GetTarget().FindBreakpointByID( + static_cast(m_nBrkPtId)); + if (brkPt.IsValid()) { + brkPt.SetIgnoreCount(m_nBrkPtCount); + + CMICmnLLDBDebugSessionInfo::SBrkPtInfo sBrkPtInfo; + if (!rSessionInfo.RecordBrkPtInfoGet(m_nBrkPtId, sBrkPtInfo)) { + SetError( + CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_BRKPT_INFO_OBJ_NOT_FOUND), + m_cmdData.strMiCmd.c_str(), m_nBrkPtId)); + return MIstatus::failure; + } + sBrkPtInfo.m_nIgnore = m_nBrkPtCount; + rSessionInfo.RecordBrkPtInfo(m_nBrkPtId, sBrkPtInfo); + } else { + const CMIUtilString strBrkPtId(CMIUtilString::Format("%d", m_nBrkPtId)); + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_BRKPT_INVALID), + m_cmdData.strMiCmd.c_str(), + strBrkPtId.c_str())); + return MIstatus::failure; + } + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdBreakAfter::Acknowledge() { + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdBreakAfter::CreateSelf() { + return new CMICmdCmdBreakAfter(); +} + + +//++ +// Details: CMICmdCmdBreakCondition constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdBreakCondition::CMICmdCmdBreakCondition() + : m_constStrArgNamedNumber("number"), m_constStrArgNamedExpr("expr"), + m_constStrArgNamedExprNoQuotes( + "expression not surround by quotes") // Not specified in MI spec, we + // need to handle expressions not + // surrounded by quotes + , + m_nBrkPtId(0) { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "break-condition"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdBreakCondition::CreateSelf; +} + +//++ +// Details: CMICmdCmdBreakCondition destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdBreakCondition::~CMICmdCmdBreakCondition() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdBreakCondition::ParseArgs() { + m_setCmdArgs.Add( + new CMICmdArgValNumber(m_constStrArgNamedNumber, true, true)); + m_setCmdArgs.Add( + new CMICmdArgValString(m_constStrArgNamedExpr, true, true, true, true)); + m_setCmdArgs.Add(new CMICmdArgValListOfN( + m_constStrArgNamedExprNoQuotes, false, false, + CMICmdArgValListBase::eArgValType_StringQuotedNumber)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdBreakCondition::Execute() { + CMICMDBASE_GETOPTION(pArgNumber, Number, m_constStrArgNamedNumber); + CMICMDBASE_GETOPTION(pArgExpr, String, m_constStrArgNamedExpr); + + m_nBrkPtId = pArgNumber->GetValue(); + m_strBrkPtExpr = pArgExpr->GetValue(); + m_strBrkPtExpr += GetRestOfExpressionNotSurroundedInQuotes(); + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBBreakpoint brkPt = rSessionInfo.GetTarget().FindBreakpointByID( + static_cast(m_nBrkPtId)); + if (brkPt.IsValid()) { + brkPt.SetCondition(m_strBrkPtExpr.c_str()); + + CMICmnLLDBDebugSessionInfo::SBrkPtInfo sBrkPtInfo; + if (!rSessionInfo.RecordBrkPtInfoGet(m_nBrkPtId, sBrkPtInfo)) { + SetError( + CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_BRKPT_INFO_OBJ_NOT_FOUND), + m_cmdData.strMiCmd.c_str(), m_nBrkPtId)); + return MIstatus::failure; + } + sBrkPtInfo.m_strCondition = m_strBrkPtExpr; + rSessionInfo.RecordBrkPtInfo(m_nBrkPtId, sBrkPtInfo); + } else { + const CMIUtilString strBrkPtId(CMIUtilString::Format("%d", m_nBrkPtId)); + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_BRKPT_INVALID), + m_cmdData.strMiCmd.c_str(), + strBrkPtId.c_str())); + return MIstatus::failure; + } + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdBreakCondition::Acknowledge() { + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdBreakCondition::CreateSelf() { + return new CMICmdCmdBreakCondition(); +} + +//++ +// Details: A breakpoint expression can be passed to *this command as: +// a single string i.e. '2' -> ok. +// a quoted string i.e. "a > 100" -> ok +// a non quoted string i.e. 'a > 100' -> not ok +// CMICmdArgValString only extracts the first space separated string, +// the "a". +// This function using the optional argument type CMICmdArgValListOfN +// collects +// the rest of the expression so that is may be added to the 'a' part +// to form a +// complete expression string i.e. "a > 100". +// If the expression value was guaranteed to be surrounded by quotes +// them this +// function would not be necessary. +// Type: Method. +// Args: None. +// Return: CMIUtilString - Rest of the breakpoint expression. +// Throws: None. +//-- +CMIUtilString +CMICmdCmdBreakCondition::GetRestOfExpressionNotSurroundedInQuotes() { + CMIUtilString strExpression; + + CMICmdArgValListOfN *pArgExprNoQuotes = + CMICmdBase::GetOption( + m_constStrArgNamedExprNoQuotes); + if (pArgExprNoQuotes != nullptr) { + const CMICmdArgValListBase::VecArgObjPtr_t &rVecExprParts( + pArgExprNoQuotes->GetExpectedOptions()); + if (!rVecExprParts.empty()) { + CMICmdArgValListBase::VecArgObjPtr_t::const_iterator it = + rVecExprParts.begin(); + while (it != rVecExprParts.end()) { + const CMICmdArgValString *pPartExpr = + static_cast(*it); + const CMIUtilString &rPartExpr = pPartExpr->GetValue(); + strExpression += " "; + strExpression += rPartExpr; + + // Next + ++it; + } + strExpression = strExpression.Trim(); + } + } + + return strExpression; +} diff --git a/src/MICmdCmdBreak.h b/src/MICmdCmdBreak.h new file mode 100644 index 00000000000..00c5aa236ea --- /dev/null +++ b/src/MICmdCmdBreak.h @@ -0,0 +1,262 @@ +//===-- MICmdCmdBreak.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdBreakInsert interface. +// CMICmdCmdBreakDelete interface. +// CMICmdCmdBreakDisable interface. +// CMICmdCmdBreakEnable interface. +// CMICmdCmdBreakAfter interface. +// CMICmdCmdBreakCondition interface. +// +// To implement new MI commands derive a new command class from the +// command base +// class. To enable the new command for interpretation add the new +// command class +// to the command factory. The files of relevance are: +// MICmdCommands.cpp +// MICmdBase.h / .cpp +// MICmdCmd.h / .cpp +// For an introduction to adding a new command see +// CMICmdCmdSupportInfoMiCmdQuery +// command class as an example. + +#pragma once + +// Third party headers: +#include "lldb/API/SBBreakpoint.h" + +// In-house headers: +#include "MICmdBase.h" + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "break-insert". +// This command does not follow the MI documentation exactly. +//-- +class CMICmdCmdBreakInsert : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdBreakInsert(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdBreakInsert() override; + + // Enumerations: +private: + //++ =================================================================== + // Details: The type of break point give in the MI command text. + //-- + enum BreakPoint_e { + eBreakPoint_Invalid = 0, + eBreakPoint_ByFileLine, + eBreakPoint_ByFileFn, + eBreakPoint_ByName, + eBreakPoint_ByAddress, + eBreakPoint_count, + eBreakPoint_NotDefineYet + }; + + // Attributes: +private: + bool m_bBrkPtIsTemp; + bool m_bHaveArgOptionThreadGrp; + CMIUtilString m_brkName; + CMIUtilString m_strArgOptionThreadGrp; + lldb::SBBreakpoint m_brkPt; + bool m_bBrkPtIsPending; + MIuint m_nBrkPtIgnoreCount; + bool m_bBrkPtEnabled; + bool m_bBrkPtCondition; + CMIUtilString m_brkPtCondition; + bool m_bBrkPtThreadId; + MIuint m_nBrkPtThreadId; + const CMIUtilString m_constStrArgNamedTempBrkPt; + const CMIUtilString m_constStrArgNamedHWBrkPt; // Not handled by *this command + const CMIUtilString m_constStrArgNamedPendinfBrkPt; + const CMIUtilString m_constStrArgNamedDisableBrkPt; + const CMIUtilString m_constStrArgNamedTracePt; // Not handled by *this command + const CMIUtilString m_constStrArgNamedConditionalBrkPt; + const CMIUtilString m_constStrArgNamedInoreCnt; + const CMIUtilString m_constStrArgNamedRestrictBrkPtToThreadId; + const CMIUtilString m_constStrArgNamedLocation; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "break-delete". +//-- +class CMICmdCmdBreakDelete : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdBreakDelete(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdBreakDelete() override; + + // Attributes: +private: + const CMIUtilString m_constStrArgNamedBrkPt; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "break-disable". +//-- +class CMICmdCmdBreakDisable : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdBreakDisable(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdBreakDisable() override; + + // Attributes: +private: + const CMIUtilString m_constStrArgNamedBrkPt; + bool m_bBrkPtDisabledOk; + MIuint m_nBrkPtId; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "break-enable". +//-- +class CMICmdCmdBreakEnable : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdBreakEnable(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdBreakEnable() override; + + // Attributes: +private: + const CMIUtilString m_constStrArgNamedBrkPt; + bool m_bBrkPtEnabledOk; + MIuint m_nBrkPtId; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "break-after". +//-- +class CMICmdCmdBreakAfter : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdBreakAfter(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdBreakAfter() override; + + // Attributes: +private: + const CMIUtilString m_constStrArgNamedNumber; + const CMIUtilString m_constStrArgNamedCount; + MIuint m_nBrkPtId; + MIuint m_nBrkPtCount; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "break-condition". +//-- +class CMICmdCmdBreakCondition : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdBreakCondition(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdBreakCondition() override; + + // Methods: +private: + CMIUtilString GetRestOfExpressionNotSurroundedInQuotes(); + + // Attributes: +private: + const CMIUtilString m_constStrArgNamedNumber; + const CMIUtilString m_constStrArgNamedExpr; + const CMIUtilString m_constStrArgNamedExprNoQuotes; // Not specified in MI + // spec, we need to handle + // expressions not + // surrounded by quotes + MIuint m_nBrkPtId; + CMIUtilString m_strBrkPtExpr; +}; diff --git a/src/MICmdCmdData.cpp b/src/MICmdCmdData.cpp new file mode 100644 index 00000000000..e0a16576519 --- /dev/null +++ b/src/MICmdCmdData.cpp @@ -0,0 +1,1673 @@ +//===-- MICmdCmdData.cpp ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdDataEvaluateExpression implementation. +// CMICmdCmdDataDisassemble implementation. +// CMICmdCmdDataReadMemoryBytes implementation. +// CMICmdCmdDataReadMemory implementation. +// CMICmdCmdDataListRegisterNames implementation. +// CMICmdCmdDataListRegisterValues implementation. +// CMICmdCmdDataListRegisterChanged implementation. +// CMICmdCmdDataWriteMemoryBytes implementation. +// CMICmdCmdDataWriteMemory implementation. +// CMICmdCmdDataInfoLine implementation. + +// Third Party Headers: +#include "lldb/API/SBInstruction.h" +#include "lldb/API/SBInstructionList.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBThread.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include +#include + +// In-house headers: +#include "MICmdArgValConsume.h" +#include "MICmdArgValListOfN.h" +#include "MICmdArgValNumber.h" +#include "MICmdArgValOptionLong.h" +#include "MICmdArgValOptionShort.h" +#include "MICmdArgValString.h" +#include "MICmdArgValThreadGrp.h" +#include "MICmdCmdData.h" +#include "MICmnLLDBDebugSessionInfo.h" +#include "MICmnLLDBDebugSessionInfoVarObj.h" +#include "MICmnLLDBDebugger.h" +#include "MICmnLLDBProxySBValue.h" +#include "MICmnLLDBUtilSBValue.h" +#include "MICmnMIResultRecord.h" +#include "MICmnMIValueConst.h" +#include "Platform.h" + +namespace { +CMIUtilString IntToHexAddrStr(uint32_t number) { + return CMIUtilString("0x" + llvm::Twine::utohexstr(number).str()); +} +} // namespace + +//++ +// Details: CMICmdCmdDataEvaluateExpression constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdDataEvaluateExpression::CMICmdCmdDataEvaluateExpression() + : m_bExpressionValid(true), m_bEvaluatedExpression(true), m_strValue("??"), + m_bFoundInvalidChar(false), m_cExpressionInvalidChar(0x00), + m_constStrArgExpr("expr") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "data-evaluate-expression"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdDataEvaluateExpression::CreateSelf; +} + +//++ +// Details: CMICmdCmdDataEvaluateExpression destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdDataEvaluateExpression::~CMICmdCmdDataEvaluateExpression() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataEvaluateExpression::ParseArgs() { + m_setCmdArgs.Add( + new CMICmdArgValString(m_constStrArgExpr, true, true, true, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataEvaluateExpression::Execute() { + CMICMDBASE_GETOPTION(pArgExpr, String, m_constStrArgExpr); + + const CMIUtilString &rExpression(pArgExpr->GetValue()); + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); + lldb::SBThread thread = sbProcess.GetSelectedThread(); + m_bExpressionValid = (thread.GetNumFrames() > 0); + if (!m_bExpressionValid) + return MIstatus::success; + + lldb::SBFrame frame = thread.GetSelectedFrame(); + lldb::SBValue value = frame.EvaluateExpression(rExpression.c_str()); + m_Error = value.GetError(); + if (!value.IsValid() || m_Error.Fail()) + value = frame.FindVariable(rExpression.c_str()); + const CMICmnLLDBUtilSBValue utilValue(value, true); + if (!utilValue.IsValid() || utilValue.IsValueUnknown()) { + m_bEvaluatedExpression = false; + return MIstatus::success; + } + if (!utilValue.HasName()) { + if (HaveInvalidCharacterInExpression(rExpression, + m_cExpressionInvalidChar)) { + m_bFoundInvalidChar = true; + return MIstatus::success; + } + + m_strValue = rExpression; + return MIstatus::success; + } + if (rExpression.IsQuoted()) { + m_strValue = rExpression.Trim('\"'); + return MIstatus::success; + } + m_strValue = utilValue.GetValue(true).Escape().AddSlashes(); + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataEvaluateExpression::Acknowledge() { + if (m_bExpressionValid) { + if (m_bEvaluatedExpression) { + if (m_bFoundInvalidChar) { + const CMICmnMIValueConst miValueConst(CMIUtilString::Format( + "Invalid character '%c' in expression", m_cExpressionInvalidChar)); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + + const CMICmnMIValueConst miValueConst(m_strValue); + const CMICmnMIValueResult miValueResult("value", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + CMIUtilString mi_error_msg = "Could not evaluate expression"; + if (const char *err_msg = m_Error.GetCString()) + mi_error_msg = err_msg; + const CMICmnMIValueConst miValueConst(mi_error_msg.Escape(true)); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + + const CMICmnMIValueConst miValueConst("Invalid expression"); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdDataEvaluateExpression::CreateSelf() { + return new CMICmdCmdDataEvaluateExpression(); +} + +//++ +// Details: Examine the expression string to see if it contains invalid +// characters. +// Type: Method. +// Args: vrExpr - (R) Expression string given to *this command. +// vrwInvalidChar - (W) True = Invalid character found, false = +// nothing found. +// Return: bool - True = Invalid character found, false = nothing found. +// Throws: None. +//-- +bool CMICmdCmdDataEvaluateExpression::HaveInvalidCharacterInExpression( + const CMIUtilString &vrExpr, char &vrwInvalidChar) { + static const std::string strInvalidCharacters(";#\\"); + const size_t nInvalidCharacterOffset = + vrExpr.find_first_of(strInvalidCharacters); + const bool bFoundInvalidCharInExpression = + (nInvalidCharacterOffset != CMIUtilString::npos); + vrwInvalidChar = + bFoundInvalidCharInExpression ? vrExpr[nInvalidCharacterOffset] : 0x00; + return bFoundInvalidCharInExpression; +} + + +//++ +// Details: CMICmdCmdDataDisassemble constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdDataDisassemble::CMICmdCmdDataDisassemble() + : m_constStrArgAddrStart("s"), m_constStrArgAddrEnd("e"), + m_constStrArgMode("mode"), m_miValueList(true) { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "data-disassemble"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdDataDisassemble::CreateSelf; +} + +//++ +// Details: CMICmdCmdDataDisassemble destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdDataDisassemble::~CMICmdCmdDataDisassemble() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataDisassemble::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValOptionShort( + m_constStrArgAddrStart, true, true, + CMICmdArgValListBase::eArgValType_StringQuotedNumber, 1)); + m_setCmdArgs.Add(new CMICmdArgValOptionShort( + m_constStrArgAddrEnd, true, true, + CMICmdArgValListBase::eArgValType_StringQuotedNumber, 1)); + m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgMode, true, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataDisassemble::Execute() { + CMICMDBASE_GETOPTION(pArgThread, OptionLong, m_constStrArgThread); + CMICMDBASE_GETOPTION(pArgAddrStart, OptionShort, m_constStrArgAddrStart); + CMICMDBASE_GETOPTION(pArgAddrEnd, OptionShort, m_constStrArgAddrEnd); + CMICMDBASE_GETOPTION(pArgMode, Number, m_constStrArgMode); + + // Retrieve the --thread option's thread ID (only 1) + MIuint64 nThreadId = UINT64_MAX; + if (pArgThread->GetFound() && + !pArgThread->GetExpectedOption(nThreadId)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_THREAD_INVALID), + m_cmdData.strMiCmd.c_str(), + m_constStrArgThread.c_str())); + return MIstatus::failure; + } + CMIUtilString strAddrStart; + if (!pArgAddrStart->GetExpectedOption( + strAddrStart)) { + SetError(CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_DISASM_ADDR_START_INVALID), + m_cmdData.strMiCmd.c_str(), m_constStrArgAddrStart.c_str())); + return MIstatus::failure; + } + MIint64 nAddrStart = 0; + if (!strAddrStart.ExtractNumber(nAddrStart)) { + SetError(CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_DISASM_ADDR_START_INVALID), + m_cmdData.strMiCmd.c_str(), m_constStrArgAddrStart.c_str())); + return MIstatus::failure; + } + + CMIUtilString strAddrEnd; + if (!pArgAddrEnd->GetExpectedOption( + strAddrEnd)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_DISASM_ADDR_END_INVALID), + m_cmdData.strMiCmd.c_str(), + m_constStrArgAddrEnd.c_str())); + return MIstatus::failure; + } + MIint64 nAddrEnd = 0; + if (!strAddrEnd.ExtractNumber(nAddrEnd)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_DISASM_ADDR_END_INVALID), + m_cmdData.strMiCmd.c_str(), + m_constStrArgAddrEnd.c_str())); + return MIstatus::failure; + } + const MIuint nDisasmMode = pArgMode->GetValue(); + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBTarget sbTarget = rSessionInfo.GetTarget(); + lldb::addr_t lldbStartAddr = static_cast(nAddrStart); + lldb::SBInstructionList instructions = sbTarget.ReadInstructions( + lldb::SBAddress(lldbStartAddr, sbTarget), nAddrEnd - nAddrStart); + const MIuint nInstructions = instructions.GetSize(); + // Calculate the offset of first instruction so that we can generate offset + // starting at 0 + lldb::addr_t start_offset = 0; + if (nInstructions > 0) + start_offset = + instructions.GetInstructionAtIndex(0).GetAddress().GetOffset(); + + for (size_t i = 0; i < nInstructions; i++) { + const char *pUnknown = "??"; + lldb::SBInstruction instrt = instructions.GetInstructionAtIndex(i); + const char *pStrMnemonic = instrt.GetMnemonic(sbTarget); + pStrMnemonic = (pStrMnemonic != nullptr) ? pStrMnemonic : pUnknown; + const char *pStrComment = instrt.GetComment(sbTarget); + CMIUtilString strComment; + if (pStrComment != nullptr && *pStrComment != '\0') + strComment = CMIUtilString::Format("; %s", pStrComment); + lldb::SBAddress address = instrt.GetAddress(); + lldb::addr_t addr = address.GetLoadAddress(sbTarget); + const char *pFnName = address.GetFunction().GetName(); + pFnName = (pFnName != nullptr) ? pFnName : pUnknown; + lldb::addr_t addrOffSet = address.GetOffset() - start_offset; + const char *pStrOperands = instrt.GetOperands(sbTarget); + pStrOperands = (pStrOperands != nullptr) ? pStrOperands : pUnknown; + const size_t instrtSize = instrt.GetByteSize(); + + // MI "{address=\"0x%016" PRIx64 + // "\",func-name=\"%s\",offset=\"%lld\",inst=\"%s %s\"}" + const CMICmnMIValueConst miValueConst( + CMIUtilString::Format("0x%016" PRIx64, addr)); + const CMICmnMIValueResult miValueResult("address", miValueConst); + CMICmnMIValueTuple miValueTuple(miValueResult); + const CMICmnMIValueConst miValueConst2(pFnName); + const CMICmnMIValueResult miValueResult2("func-name", miValueConst2); + miValueTuple.Add(miValueResult2); + const CMICmnMIValueConst miValueConst3( + CMIUtilString::Format("%lld", addrOffSet)); + const CMICmnMIValueResult miValueResult3("offset", miValueConst3); + miValueTuple.Add(miValueResult3); + const CMICmnMIValueConst miValueConst4( + CMIUtilString::Format("%d", instrtSize)); + const CMICmnMIValueResult miValueResult4("size", miValueConst4); + miValueTuple.Add(miValueResult4); + const CMICmnMIValueConst miValueConst5( + CMIUtilString::Format("%s %s%s", pStrMnemonic, pStrOperands, + strComment.Escape(true).c_str())); + const CMICmnMIValueResult miValueResult5("inst", miValueConst5); + miValueTuple.Add(miValueResult5); + + if (nDisasmMode == 1) { + lldb::SBLineEntry lineEntry = address.GetLineEntry(); + const MIuint nLine = lineEntry.GetLine(); + const char *pFileName = lineEntry.GetFileSpec().GetFilename(); + pFileName = (pFileName != nullptr) ? pFileName : pUnknown; + // Get a full path to the file. + char pathBuffer[PATH_MAX]; + lineEntry.GetFileSpec().GetPath(pathBuffer, PATH_MAX); + + // MI "src_and_asm_line={line=\"%u\",file=\"%s\",line_asm_insn=[ ], + // fullname=\"%s\"}" + const CMICmnMIValueConst miValueConst( + CMIUtilString::Format("%u", nLine)); + const CMICmnMIValueResult miValueResult("line", miValueConst); + CMICmnMIValueTuple miValueTuple2(miValueResult); + const CMICmnMIValueConst miValueConst2(pFileName); + const CMICmnMIValueResult miValueResult2("file", miValueConst2); + miValueTuple2.Add(miValueResult2); + const CMICmnMIValueList miValueList(miValueTuple); + const CMICmnMIValueResult miValueResult3("line_asm_insn", miValueList); + miValueTuple2.Add(miValueResult3); + const CMICmnMIValueConst miValueConst5(pathBuffer); + const CMICmnMIValueResult miValueResult5("fullname", miValueConst5); + miValueTuple2.Add(miValueResult5); + const CMICmnMIValueResult miValueResult4("src_and_asm_line", + miValueTuple2); + m_miValueList.Add(miValueResult4); + } else { + m_miValueList.Add(miValueTuple); + } + } + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataDisassemble::Acknowledge() { + const CMICmnMIValueResult miValueResult("asm_insns", m_miValueList); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdDataDisassemble::CreateSelf() { + return new CMICmdCmdDataDisassemble(); +} + + +//++ +// Details: CMICmdCmdDataReadMemoryBytes constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdDataReadMemoryBytes::CMICmdCmdDataReadMemoryBytes() + : m_constStrArgByteOffset("o"), m_constStrArgAddrExpr("address"), + m_constStrArgNumBytes("count"), m_pBufferMemory(nullptr), m_nAddrStart(0), + m_nAddrNumBytesToRead(0) { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "data-read-memory-bytes"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdDataReadMemoryBytes::CreateSelf; +} + +//++ +// Details: CMICmdCmdDataReadMemoryBytes destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdDataReadMemoryBytes::~CMICmdCmdDataReadMemoryBytes() { + if (m_pBufferMemory != nullptr) { + delete[] m_pBufferMemory; + m_pBufferMemory = nullptr; + } +} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataReadMemoryBytes::ParseArgs() { + m_setCmdArgs.Add( + new CMICmdArgValOptionShort(m_constStrArgByteOffset, false, true, + CMICmdArgValListBase::eArgValType_Number, 1)); + m_setCmdArgs.Add( + new CMICmdArgValString(m_constStrArgAddrExpr, true, true, true, true)); + m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgNumBytes, true, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdDataReadMemoryBytes::Execute() { + CMICMDBASE_GETOPTION(pArgThread, OptionLong, m_constStrArgThread); + CMICMDBASE_GETOPTION(pArgFrame, OptionLong, m_constStrArgFrame); + CMICMDBASE_GETOPTION(pArgAddrOffset, OptionShort, m_constStrArgByteOffset); + CMICMDBASE_GETOPTION(pArgAddrExpr, String, m_constStrArgAddrExpr); + CMICMDBASE_GETOPTION(pArgNumBytes, Number, m_constStrArgNumBytes); + + // get the --thread option value + MIuint64 nThreadId = UINT64_MAX; + if (pArgThread->GetFound() && + !pArgThread->GetExpectedOption(nThreadId)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND), + m_cmdData.strMiCmd.c_str(), + m_constStrArgThread.c_str())); + return MIstatus::failure; + } + + // get the --frame option value + MIuint64 nFrame = UINT64_MAX; + if (pArgFrame->GetFound() && + !pArgFrame->GetExpectedOption(nFrame)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND), + m_cmdData.strMiCmd.c_str(), + m_constStrArgFrame.c_str())); + return MIstatus::failure; + } + + // get the -o option value + MIuint64 nAddrOffset = 0; + if (pArgAddrOffset->GetFound() && + !pArgAddrOffset->GetExpectedOption( + nAddrOffset)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND), + m_cmdData.strMiCmd.c_str(), + m_constStrArgByteOffset.c_str())); + return MIstatus::failure; + } + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); + if (!sbProcess.IsValid()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_PROCESS), + m_cmdData.strMiCmd.c_str())); + return MIstatus::failure; + } + + lldb::SBThread thread = (nThreadId != UINT64_MAX) + ? sbProcess.GetThreadByIndexID(nThreadId) + : sbProcess.GetSelectedThread(); + if (!thread.IsValid()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_THREAD_INVALID), + m_cmdData.strMiCmd.c_str())); + return MIstatus::failure; + } + + lldb::SBFrame frame = (nFrame != UINT64_MAX) ? thread.GetFrameAtIndex(nFrame) + : thread.GetSelectedFrame(); + if (!frame.IsValid()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_FRAME_INVALID), + m_cmdData.strMiCmd.c_str())); + return MIstatus::failure; + } + + const CMIUtilString &rAddrExpr = pArgAddrExpr->GetValue(); + lldb::SBValue addrExprValue = frame.EvaluateExpression(rAddrExpr.c_str()); + lldb::SBError error = addrExprValue.GetError(); + if (error.Fail()) { + SetError(error.GetCString()); + return MIstatus::failure; + } else if (!addrExprValue.IsValid()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_EXPR_INVALID), + rAddrExpr.c_str())); + return MIstatus::failure; + } + + MIuint64 nAddrStart = 0; + if (!CMICmnLLDBProxySBValue::GetValueAsUnsigned(addrExprValue, nAddrStart)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_EXPR_INVALID), + rAddrExpr.c_str())); + return MIstatus::failure; + } + + nAddrStart += nAddrOffset; + const MIuint64 nAddrNumBytes = pArgNumBytes->GetValue(); + + m_pBufferMemory = new unsigned char[nAddrNumBytes]; + if (m_pBufferMemory == nullptr) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_MEMORY_ALLOC_FAILURE), + m_cmdData.strMiCmd.c_str(), nAddrNumBytes)); + return MIstatus::failure; + } + + const MIuint64 nReadBytes = + sbProcess.ReadMemory(static_cast(nAddrStart), + (void *)m_pBufferMemory, nAddrNumBytes, error); + if (nReadBytes != nAddrNumBytes) { + SetError(CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_LLDB_ERR_NOT_READ_WHOLE_BLK), + m_cmdData.strMiCmd.c_str(), nAddrNumBytes, nAddrStart)); + return MIstatus::failure; + } + if (error.Fail()) { + lldb::SBStream err; + const bool bOk = error.GetDescription(err); + MIunused(bOk); + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_LLDB_ERR_READ_MEM_BYTES), + m_cmdData.strMiCmd.c_str(), nAddrNumBytes, + nAddrStart, err.GetData())); + return MIstatus::failure; + } + + m_nAddrStart = nAddrStart; + m_nAddrNumBytesToRead = nAddrNumBytes; + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataReadMemoryBytes::Acknowledge() { + // MI: memory=[{begin=\"0x%016" PRIx64 "\",offset=\"0x%016" PRIx64" + // \",end=\"0x%016" PRIx64 "\",contents=\" \" }]" + const CMICmnMIValueConst miValueConst( + CMIUtilString::Format("0x%016" PRIx64, m_nAddrStart)); + const CMICmnMIValueResult miValueResult("begin", miValueConst); + CMICmnMIValueTuple miValueTuple(miValueResult); + const MIuint64 nAddrOffset = 0; + const CMICmnMIValueConst miValueConst2( + CMIUtilString::Format("0x%016" PRIx64, nAddrOffset)); + const CMICmnMIValueResult miValueResult2("offset", miValueConst2); + miValueTuple.Add(miValueResult2); + const CMICmnMIValueConst miValueConst3(CMIUtilString::Format( + "0x%016" PRIx64, m_nAddrStart + m_nAddrNumBytesToRead)); + const CMICmnMIValueResult miValueResult3("end", miValueConst3); + miValueTuple.Add(miValueResult3); + + // MI: contents=\" \" + CMIUtilString strContent; + strContent.reserve((m_nAddrNumBytesToRead << 1) + 1); + for (MIuint64 i = 0; i < m_nAddrNumBytesToRead; i++) { + strContent += CMIUtilString::Format("%02hhx", m_pBufferMemory[i]); + } + const CMICmnMIValueConst miValueConst4(strContent); + const CMICmnMIValueResult miValueResult4("contents", miValueConst4); + miValueTuple.Add(miValueResult4); + const CMICmnMIValueList miValueList(miValueTuple); + const CMICmnMIValueResult miValueResult5("memory", miValueList); + + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult5); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdDataReadMemoryBytes::CreateSelf() { + return new CMICmdCmdDataReadMemoryBytes(); +} + + +//++ +// Details: CMICmdCmdDataReadMemory constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdDataReadMemory::CMICmdCmdDataReadMemory() { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "data-read-memory"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdDataReadMemory::CreateSelf; +} + +//++ +// Details: CMICmdCmdDataReadMemory destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdDataReadMemory::~CMICmdCmdDataReadMemory() {} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataReadMemory::Execute() { + // Do nothing - command deprecated use "data-read-memory-bytes" command + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataReadMemory::Acknowledge() { + // Command CMICmdCmdSupportListFeatures sends "data-read-memory-bytes" which + // causes this command not to be called + const CMICmnMIValueConst miValueConst( + MIRSRC(IDS_CMD_ERR_NOT_IMPLEMENTED_DEPRECATED)); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdDataReadMemory::CreateSelf() { + return new CMICmdCmdDataReadMemory(); +} + + +//++ +// Details: CMICmdCmdDataListRegisterNames constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdDataListRegisterNames::CMICmdCmdDataListRegisterNames() + : m_constStrArgRegNo("regno"), m_miValueList(true) { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "data-list-register-names"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdDataListRegisterNames::CreateSelf; +} + +//++ +// Details: CMICmdCmdDataReadMemoryBytes destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdDataListRegisterNames::~CMICmdCmdDataListRegisterNames() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataListRegisterNames::ParseArgs() { + m_setCmdArgs.Add( + new CMICmdArgValListOfN(m_constStrArgRegNo, false, false, + CMICmdArgValListBase::eArgValType_Number)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataListRegisterNames::Execute() { + CMICMDBASE_GETOPTION(pArgRegNo, ListOfN, m_constStrArgRegNo); + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); + if (!sbProcess.IsValid()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_PROCESS), + m_cmdData.strMiCmd.c_str())); + return MIstatus::failure; + } + + const CMICmdArgValListBase::VecArgObjPtr_t &rVecRegNo( + pArgRegNo->GetExpectedOptions()); + if (!rVecRegNo.empty()) { + // List of required registers + CMICmdArgValListBase::VecArgObjPtr_t::const_iterator it = rVecRegNo.begin(); + while (it != rVecRegNo.end()) { + const CMICmdArgValNumber *pRegNo = static_cast(*it); + const MIuint nRegIndex = pRegNo->GetValue(); + lldb::SBValue regValue = GetRegister(nRegIndex); + if (regValue.IsValid()) { + const CMICmnMIValueConst miValueConst( + CMICmnLLDBUtilSBValue(regValue).GetName()); + m_miValueList.Add(miValueConst); + } + + // Next + ++it; + } + } else { + // List of all registers + lldb::SBThread thread = sbProcess.GetSelectedThread(); + lldb::SBFrame frame = thread.GetSelectedFrame(); + lldb::SBValueList registers = frame.GetRegisters(); + const MIuint nRegisters = registers.GetSize(); + for (MIuint i = 0; i < nRegisters; i++) { + lldb::SBValue value = registers.GetValueAtIndex(i); + const MIuint nRegChildren = value.GetNumChildren(); + for (MIuint j = 0; j < nRegChildren; j++) { + lldb::SBValue regValue = value.GetChildAtIndex(j); + if (regValue.IsValid()) { + const CMICmnMIValueConst miValueConst( + CMICmnLLDBUtilSBValue(regValue).GetName()); + m_miValueList.Add(miValueConst); + } + } + } + } + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataListRegisterNames::Acknowledge() { + const CMICmnMIValueResult miValueResult("register-names", m_miValueList); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdDataListRegisterNames::CreateSelf() { + return new CMICmdCmdDataListRegisterNames(); +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Method. +// Args: None. +// Return: lldb::SBValue - LLDB SBValue object. +// Throws: None. +//-- +lldb::SBValue +CMICmdCmdDataListRegisterNames::GetRegister(const MIuint vRegisterIndex) const { + lldb::SBThread thread = + CMICmnLLDBDebugSessionInfo::Instance().GetProcess().GetSelectedThread(); + lldb::SBFrame frame = thread.GetSelectedFrame(); + lldb::SBValueList registers = frame.GetRegisters(); + const MIuint nRegisters = registers.GetSize(); + MIuint nRegisterIndex(vRegisterIndex); + for (MIuint i = 0; i < nRegisters; i++) { + lldb::SBValue value = registers.GetValueAtIndex(i); + const MIuint nRegChildren = value.GetNumChildren(); + if (nRegisterIndex >= nRegChildren) { + nRegisterIndex -= nRegChildren; + continue; + } + + lldb::SBValue value2 = value.GetChildAtIndex(nRegisterIndex); + if (value2.IsValid()) { + return value2; + } + } + + return lldb::SBValue(); +} + + +//++ +// Details: CMICmdCmdDataListRegisterValues constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdDataListRegisterValues::CMICmdCmdDataListRegisterValues() + : m_constStrArgSkip("skip-unavailable"), m_constStrArgFormat("fmt"), + m_constStrArgRegNo("regno"), m_miValueList(true) { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "data-list-register-values"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdDataListRegisterValues::CreateSelf; +} + +//++ +// Details: CMICmdCmdDataListRegisterValues destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdDataListRegisterValues::~CMICmdCmdDataListRegisterValues() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataListRegisterValues::ParseArgs() { + m_setCmdArgs.Add( + new CMICmdArgValOptionLong(m_constStrArgThread, false, false, + CMICmdArgValListBase::eArgValType_Number, 1)); + m_setCmdArgs.Add(new CMICmdArgValOptionLong(m_constStrArgSkip, false, false)); + m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgFormat, true, true)); + m_setCmdArgs.Add( + new CMICmdArgValListOfN(m_constStrArgRegNo, false, true, + CMICmdArgValListBase::eArgValType_Number)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataListRegisterValues::Execute() { + CMICMDBASE_GETOPTION(pArgFormat, String, m_constStrArgFormat); + CMICMDBASE_GETOPTION(pArgRegNo, ListOfN, m_constStrArgRegNo); + + const CMIUtilString &rStrFormat(pArgFormat->GetValue()); + if (rStrFormat.length() != 1) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_FORMAT_TYPE), + m_cmdData.strMiCmd.c_str(), + rStrFormat.c_str())); + return MIstatus::failure; + } + const CMICmnLLDBDebugSessionInfoVarObj::varFormat_e eFormat = + CMICmnLLDBDebugSessionInfoVarObj::GetVarFormatForChar(rStrFormat[0]); + if (eFormat == CMICmnLLDBDebugSessionInfoVarObj::eVarFormat_Invalid) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_FORMAT_TYPE), + m_cmdData.strMiCmd.c_str(), + rStrFormat.c_str())); + return MIstatus::failure; + } + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); + if (!sbProcess.IsValid()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_PROCESS), + m_cmdData.strMiCmd.c_str())); + return MIstatus::failure; + } + + const CMICmdArgValListBase::VecArgObjPtr_t &rVecRegNo( + pArgRegNo->GetExpectedOptions()); + if (!rVecRegNo.empty()) { + // List of required registers + CMICmdArgValListBase::VecArgObjPtr_t::const_iterator it = rVecRegNo.begin(); + while (it != rVecRegNo.end()) { + const CMICmdArgValNumber *pRegNo = static_cast(*it); + const MIuint nRegIndex = pRegNo->GetValue(); + lldb::SBValue regValue = GetRegister(nRegIndex); + if (regValue.IsValid()) { + AddToOutput(nRegIndex, regValue, eFormat); + } + + // Next + ++it; + } + } else { + // No register numbers are provided. Output all registers. + lldb::SBThread thread = sbProcess.GetSelectedThread(); + lldb::SBFrame frame = thread.GetSelectedFrame(); + lldb::SBValueList registers = frame.GetRegisters(); + const MIuint nRegisters = registers.GetSize(); + MIuint nRegIndex = 0; + for (MIuint i = 0; i < nRegisters; i++) { + lldb::SBValue value = registers.GetValueAtIndex(i); + const MIuint nRegChildren = value.GetNumChildren(); + for (MIuint j = 0; j < nRegChildren; j++) { + lldb::SBValue regValue = value.GetChildAtIndex(j); + if (regValue.IsValid()) { + AddToOutput(nRegIndex, regValue, eFormat); + } + + // Next + ++nRegIndex; + } + } + } + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataListRegisterValues::Acknowledge() { + const CMICmnMIValueResult miValueResult("register-values", m_miValueList); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdDataListRegisterValues::CreateSelf() { + return new CMICmdCmdDataListRegisterValues(); +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Method. +// Args: None. +// Return: lldb::SBValue - LLDB SBValue object. +// Throws: None. +//-- +lldb::SBValue CMICmdCmdDataListRegisterValues::GetRegister( + const MIuint vRegisterIndex) const { + lldb::SBThread thread = + CMICmnLLDBDebugSessionInfo::Instance().GetProcess().GetSelectedThread(); + lldb::SBFrame frame = thread.GetSelectedFrame(); + lldb::SBValueList registers = frame.GetRegisters(); + const MIuint nRegisters = registers.GetSize(); + MIuint nRegisterIndex(vRegisterIndex); + for (MIuint i = 0; i < nRegisters; i++) { + lldb::SBValue value = registers.GetValueAtIndex(i); + const MIuint nRegChildren = value.GetNumChildren(); + if (nRegisterIndex >= nRegChildren) { + nRegisterIndex -= nRegChildren; + continue; + } + + lldb::SBValue value2 = value.GetChildAtIndex(nRegisterIndex); + if (value2.IsValid()) { + return value2; + } + } + + return lldb::SBValue(); +} + +//++ +// Details: Adds the register value to the output list. +// Type: Method. +// Args: Value of the register, its index and output format. +// Return: None +// Throws: None. +//-- +void CMICmdCmdDataListRegisterValues::AddToOutput( + const MIuint vnIndex, const lldb::SBValue &vrValue, + CMICmnLLDBDebugSessionInfoVarObj::varFormat_e veVarFormat) { + const CMICmnMIValueConst miValueConst(CMIUtilString::Format("%u", vnIndex)); + const CMICmnMIValueResult miValueResult("number", miValueConst); + CMICmnMIValueTuple miValueTuple(miValueResult); + const CMIUtilString strRegValue( + CMICmnLLDBDebugSessionInfoVarObj::GetValueStringFormatted(vrValue, + veVarFormat)); + const CMICmnMIValueConst miValueConst2(strRegValue); + const CMICmnMIValueResult miValueResult2("value", miValueConst2); + miValueTuple.Add(miValueResult2); + m_miValueList.Add(miValueTuple); +} + + +//++ +// Details: CMICmdCmdDataListRegisterChanged constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdDataListRegisterChanged::CMICmdCmdDataListRegisterChanged() { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "data-list-changed-registers"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdDataListRegisterChanged::CreateSelf; +} + +//++ +// Details: CMICmdCmdDataListRegisterChanged destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdDataListRegisterChanged::~CMICmdCmdDataListRegisterChanged() {} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataListRegisterChanged::Execute() { + // Do nothing + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataListRegisterChanged::Acknowledge() { + const CMICmnMIValueConst miValueConst(MIRSRC(IDS_WORD_NOT_IMPLEMENTED)); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdDataListRegisterChanged::CreateSelf() { + return new CMICmdCmdDataListRegisterChanged(); +} + + +//++ +// Details: CMICmdCmdDataWriteMemoryBytes constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdDataWriteMemoryBytes::CMICmdCmdDataWriteMemoryBytes() + : m_constStrArgAddr("address"), m_constStrArgContents("contents"), + m_constStrArgCount("count") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "data-write-memory-bytes"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdDataWriteMemoryBytes::CreateSelf; +} + +//++ +// Details: CMICmdCmdDataWriteMemoryBytes destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdDataWriteMemoryBytes::~CMICmdCmdDataWriteMemoryBytes() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataWriteMemoryBytes::ParseArgs() { + m_setCmdArgs.Add( + new CMICmdArgValString(m_constStrArgAddr, true, true, false, true)); + m_setCmdArgs.Add( + new CMICmdArgValString(m_constStrArgContents, true, true, true, true)); + m_setCmdArgs.Add( + new CMICmdArgValString(m_constStrArgCount, false, true, false, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataWriteMemoryBytes::Execute() { + // Do nothing - not reproduceable (yet) in Eclipse + // CMICMDBASE_GETOPTION( pArgOffset, OptionShort, m_constStrArgOffset ); + // CMICMDBASE_GETOPTION( pArgAddr, String, m_constStrArgAddr ); + // CMICMDBASE_GETOPTION( pArgNumber, String, m_constStrArgNumber ); + // CMICMDBASE_GETOPTION( pArgContents, String, m_constStrArgContents ); + // + // Numbers extracts as string types as they could be hex numbers + // '&' is not recognised and so has to be removed + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataWriteMemoryBytes::Acknowledge() { + const CMICmnMIValueConst miValueConst(MIRSRC(IDS_WORD_NOT_IMPLEMENTED)); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdDataWriteMemoryBytes::CreateSelf() { + return new CMICmdCmdDataWriteMemoryBytes(); +} + + +//++ +// Details: CMICmdCmdDataWriteMemory constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdDataWriteMemory::CMICmdCmdDataWriteMemory() + : m_constStrArgOffset("o"), m_constStrArgAddr("address"), + m_constStrArgD("d"), m_constStrArgNumber("a number"), + m_constStrArgContents("contents"), m_nAddr(0), m_nCount(0), + m_pBufferMemory(nullptr) { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "data-write-memory"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdDataWriteMemory::CreateSelf; +} + +//++ +// Details: CMICmdCmdDataWriteMemory destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdDataWriteMemory::~CMICmdCmdDataWriteMemory() { + if (m_pBufferMemory != nullptr) { + delete[] m_pBufferMemory; + m_pBufferMemory = nullptr; + } +} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataWriteMemory::ParseArgs() { + m_setCmdArgs.Add( + new CMICmdArgValOptionShort(m_constStrArgOffset, false, true, + CMICmdArgValListBase::eArgValType_Number, 1)); + m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgAddr, true, true)); + m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgD, true, true)); + m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgNumber, true, true)); + m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgContents, true, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataWriteMemory::Execute() { + CMICMDBASE_GETOPTION(pArgOffset, OptionShort, m_constStrArgOffset); + CMICMDBASE_GETOPTION(pArgAddr, Number, m_constStrArgAddr); + CMICMDBASE_GETOPTION(pArgNumber, Number, m_constStrArgNumber); + CMICMDBASE_GETOPTION(pArgContents, Number, m_constStrArgContents); + + MIuint nAddrOffset = 0; + if (pArgOffset->GetFound() && + !pArgOffset->GetExpectedOption(nAddrOffset)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ARGS_ERR_VALIDATION_INVALID), + m_cmdData.strMiCmd.c_str(), + m_constStrArgAddr.c_str())); + return MIstatus::failure; + } + m_nAddr = pArgAddr->GetValue(); + m_nCount = pArgNumber->GetValue(); + const MIuint64 nValue = pArgContents->GetValue(); + + m_pBufferMemory = new unsigned char[m_nCount]; + if (m_pBufferMemory == nullptr) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_MEMORY_ALLOC_FAILURE), + m_cmdData.strMiCmd.c_str(), m_nCount)); + return MIstatus::failure; + } + *m_pBufferMemory = static_cast(nValue); + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); + lldb::SBError error; + lldb::addr_t addr = static_cast(m_nAddr + nAddrOffset); + const size_t nBytesWritten = sbProcess.WriteMemory( + addr, (const void *)m_pBufferMemory, (size_t)m_nCount, error); + if (nBytesWritten != static_cast(m_nCount)) { + SetError( + CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_LLDB_ERR_NOT_WRITE_WHOLEBLK), + m_cmdData.strMiCmd.c_str(), m_nCount, addr)); + return MIstatus::failure; + } + if (error.Fail()) { + lldb::SBStream err; + const bool bOk = error.GetDescription(err); + MIunused(bOk); + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_LLDB_ERR_WRITE_MEM_BYTES), + m_cmdData.strMiCmd.c_str(), m_nCount, addr, + err.GetData())); + return MIstatus::failure; + } + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataWriteMemory::Acknowledge() { + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdDataWriteMemory::CreateSelf() { + return new CMICmdCmdDataWriteMemory(); +} + + +//++ +// Details: CMICmdCmdDataInfoLine constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdDataInfoLine::CMICmdCmdDataInfoLine() + : m_constStrArgLocation("location"), + m_resultRecord(m_cmdData.strMiCmdToken, + CMICmnMIResultRecord::eResultClass_Done) { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "data-info-line"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdDataInfoLine::CreateSelf; +} + +//++ +// Details: CMICmdCmdDataInfoLine destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdDataInfoLine::~CMICmdCmdDataInfoLine() = default; + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataInfoLine::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgLocation, true, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataInfoLine::Execute() { + CMICMDBASE_GETOPTION(pArgLocation, String, m_constStrArgLocation); + + lldb::SBLineEntry line; + bool found_line = false; + const CMIUtilString &strLocation(pArgLocation->GetValue()); + lldb::SBTarget target = CMICmnLLDBDebugSessionInfo::Instance().GetTarget(); + + if (strLocation.at(0) == '*') { + // Parse argument: + // *0x12345 + // ^^^^^^^^^ -- address + lldb::addr_t address = 0x0; + if (llvm::StringRef(strLocation.substr(1)).getAsInteger(0, address)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_SOME_ERROR), + m_cmdData.strMiCmd.c_str(), + "Failed to parse address.")); + return MIstatus::failure; + } + line = target.ResolveFileAddress(address).GetLineEntry(); + // Check that found line is valid. + if (line.GetLine()) + found_line = true; + } else { + const size_t nLineStartPos = strLocation.rfind(':'); + if ((nLineStartPos == std::string::npos) || (nLineStartPos == 0) || + (nLineStartPos == strLocation.length() - 1)) { + SetError(CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_INVALID_LOCATION_FORMAT), + m_cmdData.strMiCmd.c_str(), strLocation.c_str())); + return MIstatus::failure; + } + // Parse argument: + // hello.cpp:5 + // ^^^^^^^^^ -- file + // ^ -- line + const CMIUtilString &strFile(strLocation.substr(0, nLineStartPos)); + uint32_t numLine = 0; + llvm::StringRef(strLocation.substr(nLineStartPos + 1)) + .getAsInteger(0, numLine); + lldb::SBSymbolContextList sc_cu_list = + target.FindCompileUnits(lldb::SBFileSpec(strFile.c_str(), false)); + for (uint32_t i = 0, e = sc_cu_list.GetSize(); i < e; ++i) { + const lldb::SBCompileUnit &cu = + sc_cu_list.GetContextAtIndex(i).GetCompileUnit(); + // Break if we have already found requested line. + if (found_line) + break; + for (uint32_t j = 0, e = cu.GetNumLineEntries(); j < e; ++j) { + const lldb::SBLineEntry &curLine = cu.GetLineEntryAtIndex(j); + if (curLine.GetLine() == numLine) { + line = curLine; + found_line = true; + break; + } + } + } + } + if (!found_line) { + SetError(CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_SOME_ERROR), m_cmdData.strMiCmd.c_str(), + "The LineEntry is absent or has an unknown format.")); + return MIstatus::failure; + } + // Start address. + m_resultRecord.Add(CMICmnMIValueResult( + "start", CMICmnMIValueConst(IntToHexAddrStr( + line.GetStartAddress().GetFileAddress())))); + // End address. + m_resultRecord.Add(CMICmnMIValueResult( + "end", CMICmnMIValueConst(IntToHexAddrStr( + line.GetEndAddress().GetFileAddress())))); + // File. + std::unique_ptr upPath(new char[PATH_MAX]); + line.GetFileSpec().GetPath(upPath.get(), PATH_MAX); + m_resultRecord.Add(CMICmnMIValueResult( + "file", CMICmnMIValueConst(CMIUtilString(upPath.get())))); + // Line. + m_resultRecord.Add(CMICmnMIValueResult( + "line", CMICmnMIValueConst(std::to_string(line.GetLine())))); + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdDataInfoLine::Acknowledge() { + m_miResultRecord = m_resultRecord; + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdDataInfoLine::CreateSelf() { + return new CMICmdCmdDataInfoLine(); +} diff --git a/src/MICmdCmdData.h b/src/MICmdCmdData.h new file mode 100644 index 00000000000..19c5319faab --- /dev/null +++ b/src/MICmdCmdData.h @@ -0,0 +1,381 @@ +//===-- MICmdCmdData.h ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdDataEvaluateExpression interface. +// CMICmdCmdDataDisassemble interface. +// CMICmdCmdDataReadMemoryBytes interface. +// CMICmdCmdDataReadMemory interface. +// CMICmdCmdDataListRegisterNames interface. +// CMICmdCmdDataListRegisterValues interface. +// CMICmdCmdDataListRegisterChanged interface. +// CMICmdCmdDataWriteMemoryBytes interface. +// CMICmdCmdDataWriteMemory interface. +// CMICmdCmdDataInfoLine interface. +// +// To implement new MI commands derive a new command class from the +// command base +// class. To enable the new command for interpretation add the new +// command class +// to the command factory. The files of relevance are: +// MICmdCommands.cpp +// MICmdBase.h / .cpp +// MICmdCmd.h / .cpp +// For an introduction to adding a new command see +// CMICmdCmdSupportInfoMiCmdQuery +// command class as an example. +// + +#pragma once + +// Third party headers: +#include "lldb/API/SBError.h" + +// In-house headers: +#include "MICmdBase.h" +#include "MICmnLLDBDebugSessionInfoVarObj.h" +#include "MICmnMIValueList.h" +#include "MICmnMIValueTuple.h" +#include "MICmnMIResultRecord.h" + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "data-evaluate-expression". +//-- +class CMICmdCmdDataEvaluateExpression : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdDataEvaluateExpression(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdDataEvaluateExpression() override; + + // Methods: +private: + bool HaveInvalidCharacterInExpression(const CMIUtilString &vrExpr, + char &vrwInvalidChar); + + // Attributes: +private: + bool m_bExpressionValid; // True = yes is valid, false = not valid + bool m_bEvaluatedExpression; // True = yes is expression evaluated, false = + // failed + lldb::SBError m_Error; // Status object, which is examined when + // m_bEvaluatedExpression is false + CMIUtilString m_strValue; + CMICmnMIValueTuple m_miValueTuple; + bool m_bFoundInvalidChar; // True = yes found unexpected character in the + // expression, false = all ok + char m_cExpressionInvalidChar; + const CMIUtilString m_constStrArgExpr; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "data-disassemble". +//-- +class CMICmdCmdDataDisassemble : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdDataDisassemble(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdDataDisassemble() override; + + // Attributes: +private: + const CMIUtilString + m_constStrArgAddrStart; // MI spec non mandatory, *this command mandatory + const CMIUtilString + m_constStrArgAddrEnd; // MI spec non mandatory, *this command mandatory + const CMIUtilString m_constStrArgMode; + CMICmnMIValueList m_miValueList; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "data-read-memory-bytes". +//-- +class CMICmdCmdDataReadMemoryBytes : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdDataReadMemoryBytes(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdDataReadMemoryBytes() override; + + // Attributes: +private: + const CMIUtilString m_constStrArgByteOffset; + const CMIUtilString m_constStrArgAddrExpr; + const CMIUtilString m_constStrArgNumBytes; + unsigned char *m_pBufferMemory; + MIuint64 m_nAddrStart; + MIuint64 m_nAddrNumBytesToRead; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "data-read-memory". +//-- +class CMICmdCmdDataReadMemory : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdDataReadMemory(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdDataReadMemory() override; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "data-list-register-names". +//-- +class CMICmdCmdDataListRegisterNames : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdDataListRegisterNames(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdDataListRegisterNames() override; + + // Methods: +private: + lldb::SBValue GetRegister(const MIuint vRegisterIndex) const; + + // Attributes: +private: + const CMIUtilString m_constStrArgRegNo; // Not handled by *this command + CMICmnMIValueList m_miValueList; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "data-list-register-values". +//-- +class CMICmdCmdDataListRegisterValues : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdDataListRegisterValues(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdDataListRegisterValues() override; + + // Methods: +private: + lldb::SBValue GetRegister(const MIuint vRegisterIndex) const; + void AddToOutput(const MIuint vnIndex, const lldb::SBValue &vrValue, + CMICmnLLDBDebugSessionInfoVarObj::varFormat_e veVarFormat); + + // Attributes: +private: + const CMIUtilString m_constStrArgSkip; // Not handled by *this command + const CMIUtilString m_constStrArgFormat; + const CMIUtilString m_constStrArgRegNo; + CMICmnMIValueList m_miValueList; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "data-list-changed-registers". +//-- +class CMICmdCmdDataListRegisterChanged : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdDataListRegisterChanged(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdDataListRegisterChanged() override; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "data-read-memory-bytes". +//-- +class CMICmdCmdDataWriteMemoryBytes : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdDataWriteMemoryBytes(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdDataWriteMemoryBytes() override; + + // Attributes: +private: + const CMIUtilString m_constStrArgAddr; + const CMIUtilString m_constStrArgContents; + const CMIUtilString m_constStrArgCount; + CMIUtilString m_strContents; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "data-read-memory". +// Not specified in MI spec but Eclipse gives *this command. +//-- +class CMICmdCmdDataWriteMemory : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdDataWriteMemory(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdDataWriteMemory() override; + + // Attributes: +private: + const CMIUtilString m_constStrArgOffset; // Not specified in MI spec but + // Eclipse gives this option. + const CMIUtilString m_constStrArgAddr; // Not specified in MI spec but Eclipse + // gives this option. + const CMIUtilString + m_constStrArgD; // Not specified in MI spec but Eclipse gives this option. + const CMIUtilString m_constStrArgNumber; // Not specified in MI spec but + // Eclipse gives this option. + const CMIUtilString m_constStrArgContents; // Not specified in MI spec but + // Eclipse gives this option. + MIuint64 m_nAddr; + CMIUtilString m_strContents; + MIuint64 m_nCount; + unsigned char *m_pBufferMemory; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "data-info-line". +// See MIExtensions.txt for details. +//-- +class CMICmdCmdDataInfoLine : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdDataInfoLine(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdDataInfoLine() override; + + // Attributes: +private: + const CMIUtilString m_constStrArgLocation; + CMICmnMIResultRecord m_resultRecord; +}; diff --git a/src/MICmdCmdEnviro.cpp b/src/MICmdCmdEnviro.cpp new file mode 100644 index 00000000000..e7a92f3c9e8 --- /dev/null +++ b/src/MICmdCmdEnviro.cpp @@ -0,0 +1,145 @@ +//===-- MICmdCmdEnviro.cpp --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdEnvironmentCd implementation. + +// In-house headers: +#include "MICmdCmdEnviro.h" +#include "MICmdArgValFile.h" +#include "MICmnLLDBDebugSessionInfo.h" +#include "MICmnLLDBDebugger.h" +#include "MICmnMIResultRecord.h" +#include "MICmnMIValueConst.h" + +//++ +// Details: CMICmdCmdEnvironmentCd constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdEnvironmentCd::CMICmdCmdEnvironmentCd() + : m_constStrArgNamePathDir("pathdir") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "environment-cd"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdEnvironmentCd::CreateSelf; +} + +//++ +// Details: CMICmdCmdEnvironmentCd destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdEnvironmentCd::~CMICmdCmdEnvironmentCd() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdEnvironmentCd::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValFile(m_constStrArgNamePathDir, true, true)); + CMICmdArgContext argCntxt(m_cmdData.strMiCmdOption); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdEnvironmentCd::Execute() { + CMICMDBASE_GETOPTION(pArgPathDir, File, m_constStrArgNamePathDir); + const CMIUtilString &strWkDir(pArgPathDir->GetValue()); + CMICmnLLDBDebugger &rDbg(CMICmnLLDBDebugger::Instance()); + lldb::SBDebugger &rLldbDbg = rDbg.GetTheDebugger(); + bool bOk = rLldbDbg.SetCurrentPlatformSDKRoot(strWkDir.c_str()); + if (bOk) { + const CMIUtilString &rStrKeyWkDir( + m_rLLDBDebugSessionInfo.m_constStrSharedDataKeyWkDir); + if (!m_rLLDBDebugSessionInfo.SharedDataAdd(rStrKeyWkDir, + strWkDir)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_DBGSESSION_ERR_SHARED_DATA_ADD), + m_cmdData.strMiCmd.c_str(), + rStrKeyWkDir.c_str())); + bOk = MIstatus::failure; + } + } else + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_FNFAILED), + m_cmdData.strMiCmd.c_str(), + "SetCurrentPlatformSDKRoot()")); + + lldb::SBTarget sbTarget = m_rLLDBDebugSessionInfo.GetTarget(); + if (sbTarget.IsValid()) { + lldb::SBLaunchInfo sbLaunchInfo = sbTarget.GetLaunchInfo(); + sbLaunchInfo.SetWorkingDirectory(strWkDir.c_str()); + sbTarget.SetLaunchInfo(sbLaunchInfo); + } + + return bOk; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdEnvironmentCd::Acknowledge() { + const CMIUtilString &rStrKeyWkDir( + m_rLLDBDebugSessionInfo.m_constStrSharedDataKeyWkDir); + CMIUtilString strWkDir; + const bool bOk = m_rLLDBDebugSessionInfo.SharedDataRetrieve( + rStrKeyWkDir, strWkDir); + if (bOk) { + const CMICmnMIValueConst miValueConst(strWkDir); + const CMICmnMIValueResult miValueResult("path", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_SHARED_DATA_NOT_FOUND), + m_cmdData.strMiCmd.c_str(), + rStrKeyWkDir.c_str())); + return MIstatus::failure; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdEnvironmentCd::CreateSelf() { + return new CMICmdCmdEnvironmentCd(); +} diff --git a/src/MICmdCmdEnviro.h b/src/MICmdCmdEnviro.h new file mode 100644 index 00000000000..461ccd83a8f --- /dev/null +++ b/src/MICmdCmdEnviro.h @@ -0,0 +1,57 @@ +//===-- MICmdCmdEnviro.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdEnvironmentCd interface. +// +// To implement new MI commands derive a new command class from the +// command base +// class. To enable the new command for interpretation add the new +// command class +// to the command factory. The files of relevance are: +// MICmdCommands.cpp +// MICmdBase.h / .cpp +// MICmdCmd.h / .cpp +// For an introduction to adding a new command see +// CMICmdCmdSupportInfoMiCmdQuery +// command class as an example. + +#pragma once + +// In-house headers: +#include "MICmdBase.h" +#include "MICmnMIValueList.h" +#include "MICmnMIValueTuple.h" + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "environment-cd". +//-- +class CMICmdCmdEnvironmentCd : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdEnvironmentCd(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdEnvironmentCd() override; + + // Attributes: +private: + const CMIUtilString m_constStrArgNamePathDir; +}; diff --git a/src/MICmdCmdExec.cpp b/src/MICmdCmdExec.cpp new file mode 100644 index 00000000000..ffdf171aef0 --- /dev/null +++ b/src/MICmdCmdExec.cpp @@ -0,0 +1,1115 @@ +//===-- MICmdCmdExec.cpp ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdExecRun implementation. +// CMICmdCmdExecContinue implementation. +// CMICmdCmdExecNext implementation. +// CMICmdCmdExecStep implementation. +// CMICmdCmdExecNextInstruction implementation. +// CMICmdCmdExecStepInstruction implementation. +// CMICmdCmdExecFinish implementation. +// CMICmdCmdExecInterrupt implementation. +// CMICmdCmdExecArguments implementation. +// CMICmdCmdExecAbort implementation. + +// Third Party Headers: +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBThread.h" +#include "lldb/lldb-enumerations.h" + +// In-house headers: +#include "MICmdArgValListOfN.h" +#include "MICmdArgValNumber.h" +#include "MICmdArgValOptionLong.h" +#include "MICmdArgValOptionShort.h" +#include "MICmdArgValString.h" +#include "MICmdArgValThreadGrp.h" +#include "MICmdCmdExec.h" +#include "MICmnLLDBDebugSessionInfo.h" +#include "MICmnLLDBDebugger.h" +#include "MICmnMIOutOfBandRecord.h" +#include "MICmnMIResultRecord.h" +#include "MICmnMIValueConst.h" +#include "MICmnStreamStdout.h" +#include "MIDriver.h" + +//++ +// Details: CMICmdCmdExecRun constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdExecRun::CMICmdCmdExecRun() : m_constStrArgStart("start") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "exec-run"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdExecRun::CreateSelf; +} + +//++ +// Details: CMICmdCmdExecRun destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdExecRun::~CMICmdCmdExecRun() {} + +//++ +// Details: The invoker requires this function. It parses the command line +// options' +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdExecRun::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValOptionLong( + m_constStrArgStart, false, true, + CMICmdArgValListBase::eArgValType_OptionLong, 0)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdExecRun::Execute() { + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + + { + // Check we have a valid target. + // Note: target created via 'file-exec-and-symbols' command. + lldb::SBTarget sbTarget = rSessionInfo.GetTarget(); + if (!sbTarget.IsValid() || + sbTarget == rSessionInfo.GetDebugger().GetDummyTarget()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_TARGET_CURRENT), + m_cmdData.strMiCmd.c_str())); + return MIstatus::failure; + } + } + + lldb::SBError error; + lldb::SBStream errMsg; + lldb::SBLaunchInfo launchInfo = rSessionInfo.GetTarget().GetLaunchInfo(); + launchInfo.SetListener(rSessionInfo.GetListener()); + + // Run to first instruction or main() requested? + CMICMDBASE_GETOPTION(pArgStart, OptionLong, m_constStrArgStart); + if (pArgStart->GetFound()) { + launchInfo.SetLaunchFlags(launchInfo.GetLaunchFlags() | + lldb::eLaunchFlagStopAtEntry); + } + + lldb::SBProcess process = rSessionInfo.GetTarget().Launch(launchInfo, error); + if (!process.IsValid()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_PROCESS), + m_cmdData.strMiCmd.c_str(), + errMsg.GetData())); + return MIstatus::failure; + } + + const auto successHandler = [this] { + if (!CMIDriver::Instance().SetDriverStateRunningDebugging()) { + const CMIUtilString &rErrMsg(CMIDriver::Instance().GetErrorDescription()); + SetError(CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_SET_NEW_DRIVER_STATE), + m_cmdData.strMiCmd.c_str(), rErrMsg.c_str())); + return MIstatus::failure; + } + return MIstatus::success; + }; + + return HandleSBErrorWithSuccess(error, successHandler); +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Called only if Execute() set status as successful on completion. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdExecRun::Acknowledge() { + m_miResultRecord = CMICmnMIResultRecord( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Running); + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::pid_t pid = rSessionInfo.GetProcess().GetProcessID(); + // Give the client '=thread-group-started,id="i1" pid="xyz"' + m_bHasResultRecordExtra = true; + const CMICmnMIValueConst miValueConst2("i1"); + const CMICmnMIValueResult miValueResult2("id", miValueConst2); + const CMIUtilString strPid(CMIUtilString::Format("%lld", pid)); + const CMICmnMIValueConst miValueConst(strPid); + const CMICmnMIValueResult miValueResult("pid", miValueConst); + CMICmnMIOutOfBandRecord miOutOfBand( + CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupStarted, miValueResult2); + miOutOfBand.Add(miValueResult); + m_miResultRecordExtra = miOutOfBand.GetString(); + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdExecRun::CreateSelf() { return new CMICmdCmdExecRun(); } + + +//++ +// Details: CMICmdCmdExecContinue constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdExecContinue::CMICmdCmdExecContinue() { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "exec-continue"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdExecContinue::CreateSelf; +} + +//++ +// Details: CMICmdCmdExecContinue destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdExecContinue::~CMICmdCmdExecContinue() {} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdExecContinue::Execute() { + const auto successHandler = [this] { + // CODETAG_DEBUG_SESSION_RUNNING_PROG_RECEIVED_SIGINT_PAUSE_PROGRAM + if (!CMIDriver::Instance().SetDriverStateRunningDebugging()) { + const CMIUtilString &rErrMsg(CMIDriver::Instance().GetErrorDescription()); + SetError(CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_SET_NEW_DRIVER_STATE), + m_cmdData.strMiCmd.c_str(), rErrMsg.c_str())); + return MIstatus::failure; + } + return MIstatus::success; + }; + + return HandleSBErrorWithSuccess( + CMICmnLLDBDebugSessionInfo::Instance().GetProcess().Continue(), + successHandler); +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdExecContinue::Acknowledge() { + m_miResultRecord = CMICmnMIResultRecord( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Running); + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdExecContinue::CreateSelf() { + return new CMICmdCmdExecContinue(); +} + + +//++ +// Details: CMICmdCmdExecNext constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdExecNext::CMICmdCmdExecNext() : m_constStrArgNumber("number") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "exec-next"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdExecNext::CreateSelf; +} + +//++ +// Details: CMICmdCmdExecNext destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdExecNext::~CMICmdCmdExecNext() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdExecNext::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgNumber, false, false)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdExecNext::Execute() { + CMICMDBASE_GETOPTION(pArgThread, OptionLong, m_constStrArgThread); + + // Retrieve the --thread option's thread ID (only 1) + MIuint64 nThreadId = UINT64_MAX; + if (pArgThread->GetFound() && + !pArgThread->GetExpectedOption(nThreadId)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_THREAD_INVALID), + m_cmdData.strMiCmd.c_str(), + m_constStrArgThread.c_str())); + return MIstatus::failure; + } + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + + lldb::SBError error; + if (nThreadId != UINT64_MAX) { + lldb::SBThread sbThread = rSessionInfo.GetProcess().GetThreadByIndexID(nThreadId); + if (!sbThread.IsValid()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_THREAD_INVALID), + m_cmdData.strMiCmd.c_str(), + m_constStrArgThread.c_str())); + return MIstatus::failure; + } + sbThread.StepOver(lldb::eOnlyDuringStepping, error); + } else + rSessionInfo.GetProcess().GetSelectedThread().StepOver( + lldb::eOnlyDuringStepping, error); + + return HandleSBError(error); +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdExecNext::Acknowledge() { + m_miResultRecord = CMICmnMIResultRecord( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Running); + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdExecNext::CreateSelf() { return new CMICmdCmdExecNext(); } + + +//++ +// Details: CMICmdCmdExecStep constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdExecStep::CMICmdCmdExecStep() : m_constStrArgNumber("number") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "exec-step"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdExecStep::CreateSelf; +} + +//++ +// Details: CMICmdCmdExecStep destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdExecStep::~CMICmdCmdExecStep() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdExecStep::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgNumber, false, false)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdExecStep::Execute() { + CMICMDBASE_GETOPTION(pArgThread, OptionLong, m_constStrArgThread); + + // Retrieve the --thread option's thread ID (only 1) + MIuint64 nThreadId = UINT64_MAX; + if (pArgThread->GetFound() && + !pArgThread->GetExpectedOption(nThreadId)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND), + m_cmdData.strMiCmd.c_str(), + m_constStrArgThread.c_str())); + return MIstatus::failure; + } + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + + lldb::SBError error; + if (nThreadId != UINT64_MAX) { + lldb::SBThread sbThread = + rSessionInfo.GetProcess().GetThreadByIndexID(nThreadId); + if (!sbThread.IsValid()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_THREAD_INVALID), + m_cmdData.strMiCmd.c_str(), + m_constStrArgThread.c_str())); + return MIstatus::failure; + } + sbThread.StepInto(nullptr, LLDB_INVALID_LINE_NUMBER, error); + } else + rSessionInfo.GetProcess().GetSelectedThread().StepInto( + nullptr, LLDB_INVALID_LINE_NUMBER, error); + + return HandleSBError(error); +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdExecStep::Acknowledge() { + m_miResultRecord = CMICmnMIResultRecord( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Running); + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdExecStep::CreateSelf() { return new CMICmdCmdExecStep(); } + + +//++ +// Details: CMICmdCmdExecNextInstruction constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdExecNextInstruction::CMICmdCmdExecNextInstruction() + : m_constStrArgNumber("number") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "exec-next-instruction"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdExecNextInstruction::CreateSelf; +} + +//++ +// Details: CMICmdCmdExecNextInstruction destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdExecNextInstruction::~CMICmdCmdExecNextInstruction() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdExecNextInstruction::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgNumber, false, false)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdExecNextInstruction::Execute() { + CMICMDBASE_GETOPTION(pArgThread, OptionLong, m_constStrArgThread); + + // Retrieve the --thread option's thread ID (only 1) + MIuint64 nThreadId = UINT64_MAX; + if (pArgThread->GetFound() && + !pArgThread->GetExpectedOption(nThreadId)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND), + m_cmdData.strMiCmd.c_str(), + m_constStrArgThread.c_str())); + return MIstatus::failure; + } + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + + lldb::SBError error; + if (nThreadId != UINT64_MAX) { + lldb::SBThread sbThread = + rSessionInfo.GetProcess().GetThreadByIndexID(nThreadId); + if (!sbThread.IsValid()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_THREAD_INVALID), + m_cmdData.strMiCmd.c_str(), + m_constStrArgThread.c_str())); + return MIstatus::failure; + } + sbThread.StepInstruction(true, error); + } else + rSessionInfo.GetProcess().GetSelectedThread().StepInstruction( + true, error); + + return HandleSBError(error); +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdExecNextInstruction::Acknowledge() { + m_miResultRecord = CMICmnMIResultRecord( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Running); + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdExecNextInstruction::CreateSelf() { + return new CMICmdCmdExecNextInstruction(); +} + + +//++ +// Details: CMICmdCmdExecStepInstruction constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdExecStepInstruction::CMICmdCmdExecStepInstruction() + : m_constStrArgNumber("number") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "exec-step-instruction"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdExecStepInstruction::CreateSelf; +} + +//++ +// Details: CMICmdCmdExecStepInstruction destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdExecStepInstruction::~CMICmdCmdExecStepInstruction() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdExecStepInstruction::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgNumber, false, false)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdExecStepInstruction::Execute() { + CMICMDBASE_GETOPTION(pArgThread, OptionLong, m_constStrArgThread); + + // Retrieve the --thread option's thread ID (only 1) + MIuint64 nThreadId = UINT64_MAX; + if (pArgThread->GetFound() && + !pArgThread->GetExpectedOption(nThreadId)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND), + m_cmdData.strMiCmd.c_str(), + m_constStrArgThread.c_str())); + return MIstatus::failure; + } + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + + lldb::SBError error; + if (nThreadId != UINT64_MAX) { + lldb::SBThread sbThread = + rSessionInfo.GetProcess().GetThreadByIndexID(nThreadId); + if (!sbThread.IsValid()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_THREAD_INVALID), + m_cmdData.strMiCmd.c_str(), + m_constStrArgThread.c_str())); + return MIstatus::failure; + } + sbThread.StepInstruction(false, error); + } else + rSessionInfo.GetProcess().GetSelectedThread().StepInstruction( + false, error); + + return HandleSBError(error); +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdExecStepInstruction::Acknowledge() { + m_miResultRecord = CMICmnMIResultRecord( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Running); + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdExecStepInstruction::CreateSelf() { + return new CMICmdCmdExecStepInstruction(); +} + + +//++ +// Details: CMICmdCmdExecFinish constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdExecFinish::CMICmdCmdExecFinish() { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "exec-finish"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdExecFinish::CreateSelf; +} + +//++ +// Details: CMICmdCmdExecFinish destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdExecFinish::~CMICmdCmdExecFinish() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdExecFinish::ParseArgs() { return ParseValidateCmdOptions(); } + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdExecFinish::Execute() { + CMICMDBASE_GETOPTION(pArgThread, OptionLong, m_constStrArgThread); + + // Retrieve the --thread option's thread ID (only 1) + MIuint64 nThreadId = UINT64_MAX; + if (pArgThread->GetFound() && + !pArgThread->GetExpectedOption(nThreadId)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND), + m_cmdData.strMiCmd.c_str(), + m_constStrArgThread.c_str())); + return MIstatus::failure; + } + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + + lldb::SBError error; + if (nThreadId != UINT64_MAX) { + lldb::SBThread sbThread = + rSessionInfo.GetProcess().GetThreadByIndexID(nThreadId); + if (!sbThread.IsValid()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_THREAD_INVALID), + m_cmdData.strMiCmd.c_str(), + m_constStrArgThread.c_str())); + return MIstatus::failure; + } + sbThread.StepOut(error); + } else + rSessionInfo.GetProcess().GetSelectedThread().StepOut(error); + + return HandleSBError(error); +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdExecFinish::Acknowledge() { + m_miResultRecord = CMICmnMIResultRecord( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Running); + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdExecFinish::CreateSelf() { + return new CMICmdCmdExecFinish(); +} + + +//++ +// Details: CMICmdCmdExecInterrupt constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdExecInterrupt::CMICmdCmdExecInterrupt() { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "exec-interrupt"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdExecInterrupt::CreateSelf; +} + +//++ +// Details: CMICmdCmdExecInterrupt destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdExecInterrupt::~CMICmdCmdExecInterrupt() {} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdExecInterrupt::Execute() { + const auto successHandler = [this] { + // CODETAG_DEBUG_SESSION_RUNNING_PROG_RECEIVED_SIGINT_PAUSE_PROGRAM + if (!CMIDriver::Instance().SetDriverStateRunningNotDebugging()) { + const CMIUtilString &rErrMsg(CMIDriver::Instance().GetErrorDescription()); + SetErrorDescription(CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_SET_NEW_DRIVER_STATE), + m_cmdData.strMiCmd.c_str(), + rErrMsg.c_str())); + return MIstatus::failure; + } + return MIstatus::success; + }; + + return HandleSBErrorWithSuccess( + CMICmnLLDBDebugSessionInfo::Instance().GetProcess().Stop(), + successHandler); +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdExecInterrupt::Acknowledge() { + m_miResultRecord = CMICmnMIResultRecord( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdExecInterrupt::CreateSelf() { + return new CMICmdCmdExecInterrupt(); +} + + +//++ +// Details: CMICmdCmdExecArguments constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdExecArguments::CMICmdCmdExecArguments() + : m_constStrArgArguments("arguments") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "exec-arguments"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdExecArguments::CreateSelf; +} + +//++ +// Details: CMICmdCmdExecArguments destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdExecArguments::~CMICmdCmdExecArguments() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdExecArguments::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValListOfN( + m_constStrArgArguments, false, true, + CMICmdArgValListBase::eArgValType_StringAnything)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdExecArguments::Execute() { + CMICMDBASE_GETOPTION(pArgArguments, ListOfN, m_constStrArgArguments); + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBTarget sbTarget = rSessionInfo.GetTarget(); + if (!sbTarget.IsValid()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_TARGET_CURRENT), + m_cmdData.strMiCmd.c_str())); + return MIstatus::failure; + } + + lldb::SBLaunchInfo sbLaunchInfo = sbTarget.GetLaunchInfo(); + sbLaunchInfo.SetArguments(nullptr, false); + + CMIUtilString strArg; + size_t nArgIndex = 0; + while (pArgArguments->GetExpectedOption( + strArg, nArgIndex)) { + const char *argv[2] = {strArg.c_str(), nullptr}; + sbLaunchInfo.SetArguments(argv, true); + ++nArgIndex; + } + + sbTarget.SetLaunchInfo(sbLaunchInfo); + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdExecArguments::Acknowledge() { + m_miResultRecord = CMICmnMIResultRecord( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdExecArguments::CreateSelf() { + return new CMICmdCmdExecArguments(); +} + + +//++ +// Details: CMICmdCmdExecAbort constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdExecAbort::CMICmdCmdExecAbort() { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "exec-abort"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdExecAbort::CreateSelf; +} + +//++ +// Details: CMICmdCmdExecAbort destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdExecAbort::~CMICmdCmdExecAbort() {} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdExecAbort::Execute() { + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); + if (!sbProcess.IsValid()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_PROCESS), + m_cmdData.strMiCmd.c_str())); + return MIstatus::failure; + } + + lldb::SBError sbError = sbProcess.Destroy(); + if (sbError.Fail()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_LLDBPROCESS_DESTROY), + m_cmdData.strMiCmd.c_str(), + sbError.GetCString())); + return MIstatus::failure; + } + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdExecAbort::Acknowledge() { + m_miResultRecord = CMICmnMIResultRecord( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdExecAbort::CreateSelf() { + return new CMICmdCmdExecAbort(); +} diff --git a/src/MICmdCmdExec.h b/src/MICmdCmdExec.h new file mode 100644 index 00000000000..8533915e24f --- /dev/null +++ b/src/MICmdCmdExec.h @@ -0,0 +1,316 @@ +//===-- MICmdCmdExec.h ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdExecRun interface. +// CMICmdCmdExecContinue interface. +// CMICmdCmdExecNext interface. +// CMICmdCmdExecStep interface. +// CMICmdCmdExecNextInstruction interface. +// CMICmdCmdExecStepInstruction interface. +// CMICmdCmdExecFinish interface. +// CMICmdCmdExecInterrupt interface. +// CMICmdCmdExecArguments interface. +// CMICmdCmdExecAbort interface. +// +// To implement new MI commands derive a new command class from the +// command base +// class. To enable the new command for interpretation add the new +// command class +// to the command factory. The files of relevance are: +// MICmdCommands.cpp +// MICmdBase.h / .cpp +// MICmdCmd.h / .cpp +// For an introduction to adding a new command see +// CMICmdCmdSupportInfoMiCmdQuery +// command class as an example. + +#pragma once + +// Third party headers: +// In-house headers: +#include "MICmdBase.h" + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "exec-run". +//-- +class CMICmdCmdExecRun : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdExecRun(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdExecRun() override; + + // Attributes: +private: + const CMIUtilString m_constStrArgStart; // StopAtEntry - run to first + // instruction or main() if specified +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "exec-continue". +//-- +class CMICmdCmdExecContinue : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdExecContinue(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdExecContinue() override; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "exec-next". +//-- +class CMICmdCmdExecNext : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdExecNext(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdExecNext() override; + + // Attributes: +private: + const CMIUtilString m_constStrArgNumber; // Not specified in MI spec but + // Eclipse gives this option +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "exec-step". +//-- +class CMICmdCmdExecStep : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdExecStep(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdExecStep() override; + + // Attributes: +private: + const CMIUtilString m_constStrArgNumber; // Not specified in MI spec but + // Eclipse gives this option +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "exec-next-instruction". +//-- +class CMICmdCmdExecNextInstruction : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdExecNextInstruction(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdExecNextInstruction() override; + + // Attributes: +private: + const CMIUtilString m_constStrArgNumber; // Not specified in MI spec but + // Eclipse gives this option +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "exec-step-instruction". +//-- +class CMICmdCmdExecStepInstruction : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdExecStepInstruction(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdExecStepInstruction() override; + + // Attributes: +private: + const CMIUtilString m_constStrArgNumber; // Not specified in MI spec but + // Eclipse gives this option +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "exec-finish". +//-- +class CMICmdCmdExecFinish : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdExecFinish(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdExecFinish() override; +}; + +// CODETAG_DEBUG_SESSION_RUNNING_PROG_RECEIVED_SIGINT_PAUSE_PROGRAM +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "exec-interrupt". +// Gotchas: Using Eclipse this command is injected into the command system when +// a +// SIGINT signal is received while running an inferior program. +//-- +class CMICmdCmdExecInterrupt : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdExecInterrupt(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdExecInterrupt() override; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "exec-arguments". +//-- +class CMICmdCmdExecArguments : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdExecArguments(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdExecArguments() override; + + // Attributes: +private: + const CMIUtilString m_constStrArgArguments; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "exec-abort". +//-- +class CMICmdCmdExecAbort : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdExecAbort(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdExecAbort() override; +}; diff --git a/src/MICmdCmdFile.cpp b/src/MICmdCmdFile.cpp new file mode 100644 index 00000000000..8b105eb1d58 --- /dev/null +++ b/src/MICmdCmdFile.cpp @@ -0,0 +1,206 @@ +//===-- MICmdCmdFile.cpp ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdFileExecAndSymbols implementation. + +// Third Party Headers: +#include "lldb/API/SBStream.h" + +// In-house headers: +#include "MICmdArgValFile.h" +#include "MICmdArgValOptionLong.h" +#include "MICmdArgValOptionShort.h" +#include "MICmdArgValString.h" +#include "MICmdCmdFile.h" +#include "MICmnLLDBDebugSessionInfo.h" +#include "MICmnLLDBDebugger.h" +#include "MICmnMIResultRecord.h" +#include "MIUtilFileStd.h" + +//++ +// Details: CMICmdCmdFileExecAndSymbols constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdFileExecAndSymbols::CMICmdCmdFileExecAndSymbols() + : m_constStrArgNameFile("file"), m_constStrArgNamedPlatformName("p"), + m_constStrArgNamedRemotePath("r") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "file-exec-and-symbols"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdFileExecAndSymbols::CreateSelf; +} + +//++ +// Details: CMICmdCmdFileExecAndSymbols destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdFileExecAndSymbols::~CMICmdCmdFileExecAndSymbols() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdFileExecAndSymbols::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValFile(m_constStrArgNameFile, true, true)); + m_setCmdArgs.Add( + new CMICmdArgValOptionShort(m_constStrArgNamedPlatformName, false, true, + CMICmdArgValListBase::eArgValType_String, 1)); + m_setCmdArgs.Add(new CMICmdArgValOptionShort( + m_constStrArgNamedRemotePath, false, true, + CMICmdArgValListBase::eArgValType_StringQuotedNumberPath, 1)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Synopsis: -file-exec-and-symbols file +// Ref: +// http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-File-Commands.html#GDB_002fMI-File-Commands +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdFileExecAndSymbols::Execute() { + CMICMDBASE_GETOPTION(pArgNamedFile, File, m_constStrArgNameFile); + CMICMDBASE_GETOPTION(pArgPlatformName, OptionShort, + m_constStrArgNamedPlatformName); + CMICMDBASE_GETOPTION(pArgRemotePath, OptionShort, + m_constStrArgNamedRemotePath); + CMICmdArgValFile *pArgFile = static_cast(pArgNamedFile); + const CMIUtilString &strExeFilePath(pArgFile->GetValue()); + bool bPlatformName = pArgPlatformName->GetFound(); + CMIUtilString platformName; + if (bPlatformName) { + pArgPlatformName->GetExpectedOption( + platformName); + } + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBDebugger &rDbgr = rSessionInfo.GetDebugger(); + lldb::SBError error; + const char *pTargetTriple = nullptr; // Let LLDB discover the triple required + const char *pTargetPlatformName = platformName.c_str(); + const bool bAddDepModules = false; + lldb::SBTarget target = + rDbgr.CreateTarget(strExeFilePath.c_str(), pTargetTriple, + pTargetPlatformName, bAddDepModules, error); + CMIUtilString strWkDir; + const CMIUtilString &rStrKeyWkDir(rSessionInfo.m_constStrSharedDataKeyWkDir); + if (!rSessionInfo.SharedDataRetrieve(rStrKeyWkDir, strWkDir)) { + strWkDir = CMIUtilFileStd::StripOffFileName(strExeFilePath); + if (!rSessionInfo.SharedDataAdd(rStrKeyWkDir, strWkDir)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_DBGSESSION_ERR_SHARED_DATA_ADD), + m_cmdData.strMiCmd.c_str(), + rStrKeyWkDir.c_str())); + return MIstatus::failure; + } + } + if (!rDbgr.SetCurrentPlatformSDKRoot(strWkDir.c_str())) { + + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_FNFAILED), + m_cmdData.strMiCmd.c_str(), + "SetCurrentPlatformSDKRoot()")); + return MIstatus::failure; + } + if (pArgRemotePath->GetFound()) { + CMIUtilString remotePath; + pArgRemotePath->GetExpectedOption( + remotePath); + lldb::SBModule module = target.FindModule(target.GetExecutable()); + if (module.IsValid()) { + module.SetPlatformFileSpec(lldb::SBFileSpec(remotePath.c_str())); + } + } + lldb::SBStream err; + if (error.Fail()) { + const bool bOk = error.GetDescription(err); + MIunused(bOk); + } + if (!target.IsValid()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_TARGET), + m_cmdData.strMiCmd.c_str(), + strExeFilePath.c_str(), err.GetData())); + return MIstatus::failure; + } + if (error.Fail()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_CREATE_TARGET), + m_cmdData.strMiCmd.c_str(), err.GetData())); + return MIstatus::failure; + } + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdFileExecAndSymbols::Acknowledge() { + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdFileExecAndSymbols::CreateSelf() { + return new CMICmdCmdFileExecAndSymbols(); +} + +//++ +// Details: If the MI Driver is not operating via a client i.e. Eclipse but say +// operating +// on a executable passed in as a argument to the drive then what +// should the driver +// do on a command failing? Either continue operating or exit the +// application. +// Override this function where a command failure cannot allow the +// driver to +// continue operating. +// Type: Overridden. +// Args: None. +// Return: bool - True = Fatal if command fails, false = can continue if +// command fails. +// Throws: None. +//-- +bool CMICmdCmdFileExecAndSymbols::GetExitAppOnCommandFailure() const { + return true; +} diff --git a/src/MICmdCmdFile.h b/src/MICmdCmdFile.h new file mode 100644 index 00000000000..4607f748cba --- /dev/null +++ b/src/MICmdCmdFile.h @@ -0,0 +1,66 @@ +//===-- MICmdCmdFile.h ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdFileExecAndSymbols interface. +// +// To implement new MI commands derive a new command class from the +// command base +// class. To enable the new command for interpretation add the new +// command class +// to the command factory. The files of relevance are: +// MICmdCommands.cpp +// MICmdBase.h / .cpp +// MICmdCmd.h / .cpp +// For an introduction to adding a new command see +// CMICmdCmdSupportInfoMiCmdQuery +// command class as an example. + +#pragma once + +// In-house headers: +#include "MICmdBase.h" +#include "MICmnMIValueList.h" +#include "MICmnMIValueTuple.h" + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "file-exec-and-symbols". +// This command does not follow the MI documentation exactly. +// Gotchas: This command has additional flags that were not available in GDB MI. +// See MIextensions.txt for details. +//-- +class CMICmdCmdFileExecAndSymbols : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdFileExecAndSymbols(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdFileExecAndSymbols() override; + bool GetExitAppOnCommandFailure() const override; + + // Attributes: +private: + const CMIUtilString m_constStrArgNameFile; + const CMIUtilString + m_constStrArgNamedPlatformName; // Added to support iOS platform selection + const CMIUtilString m_constStrArgNamedRemotePath; // Added to support iOS + // device remote file + // location +}; diff --git a/src/MICmdCmdGdbInfo.cpp b/src/MICmdCmdGdbInfo.cpp new file mode 100644 index 00000000000..b351353ba1e --- /dev/null +++ b/src/MICmdCmdGdbInfo.cpp @@ -0,0 +1,225 @@ +//===-- MICmdCmdGdbInfo.cpp -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdGdbInfo implementation. + +// Third party headers: +#include "lldb/API/SBCommandReturnObject.h" +#include + +// In-house headers: +#include "MICmdArgValString.h" +#include "MICmdCmdGdbInfo.h" +#include "MICmnLLDBDebugSessionInfo.h" +#include "MICmnMIResultRecord.h" +#include "MICmnMIValueConst.h" +#include "MICmnStreamStdout.h" + +// Instantiations: +const CMICmdCmdGdbInfo::MapPrintFnNameToPrintFn_t + CMICmdCmdGdbInfo::ms_mapPrintFnNameToPrintFn = { + {"sharedlibrary", &CMICmdCmdGdbInfo::PrintFnSharedLibrary}}; + +//++ +// Details: CMICmdCmdGdbInfo constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdGdbInfo::CMICmdCmdGdbInfo() + : m_constStrArgNamedPrint("print"), m_bPrintFnRecognised(true), + m_bPrintFnSuccessful(false), + m_strPrintFnError(MIRSRC(IDS_WORD_ERR_MSG_NOT_IMPLEMENTED_BRKTS)) { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "info"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdGdbInfo::CreateSelf; +} + +//++ +// Details: CMICmdCmdGdbInfo destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdGdbInfo::~CMICmdCmdGdbInfo() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdGdbInfo::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgNamedPrint, true, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdGdbInfo::Execute() { + CMICMDBASE_GETOPTION(pArgPrint, String, m_constStrArgNamedPrint); + const CMIUtilString &rPrintRequest(pArgPrint->GetValue()); + + FnPrintPtr pPrintRequestFn = nullptr; + if (!GetPrintFn(rPrintRequest, pPrintRequestFn)) { + m_strPrintFnName = rPrintRequest; + m_bPrintFnRecognised = false; + return MIstatus::success; + } + + m_bPrintFnSuccessful = (this->*(pPrintRequestFn))(); + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdGdbInfo::Acknowledge() { + if (!m_bPrintFnRecognised) { + const CMICmnMIValueConst miValueConst(CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_INFO_PRINTFN_NOT_FOUND), m_strPrintFnName.c_str())); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + + if (m_bPrintFnSuccessful) { + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + + const CMICmnMIValueConst miValueConst(CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_INFO_PRINTFN_FAILED), m_strPrintFnError.c_str())); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdGdbInfo::CreateSelf() { return new CMICmdCmdGdbInfo(); } + +//++ +// Details: Retrieve the print function's pointer for the matching print +// request. +// Type: Method. +// Args: vrPrintFnName - (R) The info requested. +// vrwpFn - (W) The print function's pointer of the function +// to carry out +// Return: bool - True = Print request is implemented, false = not found. +// Throws: None. +//-- +bool CMICmdCmdGdbInfo::GetPrintFn(const CMIUtilString &vrPrintFnName, + FnPrintPtr &vrwpFn) const { + vrwpFn = nullptr; + + const MapPrintFnNameToPrintFn_t::const_iterator it = + ms_mapPrintFnNameToPrintFn.find(vrPrintFnName); + if (it != ms_mapPrintFnNameToPrintFn.end()) { + vrwpFn = (*it).second; + return true; + } + + return false; +} + +//++ +// Details: Carry out work to complete the request to prepare and send back +// information +// asked for. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdGdbInfo::PrintFnSharedLibrary() { + bool bOk = CMICmnStreamStdout::TextToStdout( + "~\"From To Syms Read Shared Object Library\""); + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBTarget sbTarget = rSessionInfo.GetTarget(); + const MIuint nModules = sbTarget.GetNumModules(); + for (MIuint i = 0; bOk && (i < nModules); i++) { + lldb::SBModule module = sbTarget.GetModuleAtIndex(i); + if (module.IsValid()) { + const CMIUtilString strModuleFilePath( + module.GetFileSpec().GetDirectory()); + const CMIUtilString strModuleFileName(module.GetFileSpec().GetFilename()); + const CMIUtilString strModuleFullPath(CMIUtilString::Format( + "%s/%s", strModuleFilePath.c_str(), strModuleFileName.c_str())); + const CMIUtilString strHasSymbols = + (module.GetNumSymbols() > 0) ? "Yes" : "No"; + lldb::addr_t addrLoadS = 0xffffffffffffffff; + lldb::addr_t addrLoadSize = 0; + bool bHaveAddrLoad = false; + const MIuint nSections = module.GetNumSections(); + for (MIuint j = 0; j < nSections; j++) { + lldb::SBSection section = module.GetSectionAtIndex(j); + lldb::addr_t addrLoad = section.GetLoadAddress(sbTarget); + if (addrLoad != (lldb::addr_t)-1) { + if (!bHaveAddrLoad) { + bHaveAddrLoad = true; + addrLoadS = addrLoad; + } + + addrLoadSize += section.GetByteSize(); + } + } + bOk = bOk && + CMICmnStreamStdout::TextToStdout(CMIUtilString::Format( + "~\"0x%016" PRIx64 "\t0x%016" PRIx64 "\t%s\t\t%s\"", addrLoadS, + addrLoadS + addrLoadSize, strHasSymbols.c_str(), + strModuleFullPath.c_str())); + } + } + + return bOk; +} diff --git a/src/MICmdCmdGdbInfo.h b/src/MICmdCmdGdbInfo.h new file mode 100644 index 00000000000..8ad69c49f59 --- /dev/null +++ b/src/MICmdCmdGdbInfo.h @@ -0,0 +1,87 @@ +//===-- MICmdCmdGdbInfo.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdGdbInfo interface. +// +// To implement new MI commands derive a new command class from the +// command base +// class. To enable the new command for interpretation add the new +// command class +// to the command factory. The files of relevance are: +// MICmdCommands.cpp +// MICmdBase.h / .cpp +// MICmdCmd.h / .cpp +// For an introduction to adding a new command see +// CMICmdCmdSupportInfoMiCmdQuery +// command class as an example. + +#pragma once + +// Third party headers: +#include + +// In-house headers: +#include "MICmdBase.h" + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements GDB command "info". +// The design of matching the info request to a request action (or +// command) is very simple. The request function which carries out +// the task of information gathering and printing to stdout is part of +// *this class. Should the request function become more complicated +// then +// that request should really reside in a command type class. Then this +// class instantiates a request info command for a matching request. +// The +// design/code of *this class then does not then become bloated. Use a +// lightweight version of the current MI command system. +//-- +class CMICmdCmdGdbInfo : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdGdbInfo(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdGdbInfo() override; + + // Typedefs: +private: + typedef bool (CMICmdCmdGdbInfo::*FnPrintPtr)(); + typedef std::map MapPrintFnNameToPrintFn_t; + + // Methods: +private: + bool GetPrintFn(const CMIUtilString &vrPrintFnName, FnPrintPtr &vrwpFn) const; + bool PrintFnSharedLibrary(); + + // Attributes: +private: + const static MapPrintFnNameToPrintFn_t ms_mapPrintFnNameToPrintFn; + // + const CMIUtilString m_constStrArgNamedPrint; + bool m_bPrintFnRecognised; // True = This command has a function with a name + // that matches the Print argument, false = not + // found + bool m_bPrintFnSuccessful; // True = The print function completed its task ok, + // false = function failed for some reason + CMIUtilString m_strPrintFnName; + CMIUtilString m_strPrintFnError; +}; diff --git a/src/MICmdCmdGdbSet.cpp b/src/MICmdCmdGdbSet.cpp new file mode 100644 index 00000000000..162e3d5a345 --- /dev/null +++ b/src/MICmdCmdGdbSet.cpp @@ -0,0 +1,491 @@ +//===-- MICmdCmdGdbSet.cpp --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdGdbSet implementation. + +// In-house headers: +#include "MICmdCmdGdbSet.h" +#include "MICmdArgValListOfN.h" +#include "MICmdArgValOptionLong.h" +#include "MICmdArgValString.h" +#include "MICmnLLDBDebugSessionInfo.h" +#include "MICmnMIResultRecord.h" +#include "MICmnMIValueConst.h" + +// Instantiations: +const CMICmdCmdGdbSet::MapGdbOptionNameToFnGdbOptionPtr_t + CMICmdCmdGdbSet::ms_mapGdbOptionNameToFnGdbOptionPtr = { + {"target-async", &CMICmdCmdGdbSet::OptionFnTargetAsync}, + {"print", &CMICmdCmdGdbSet::OptionFnPrint}, + // { "auto-solib-add", &CMICmdCmdGdbSet::OptionFnAutoSolibAdd }, // + // Example code if need to implement GDB set other options + {"output-radix", &CMICmdCmdGdbSet::OptionFnOutputRadix}, + {"solib-search-path", &CMICmdCmdGdbSet::OptionFnSolibSearchPath}, + {"disassembly-flavor", &CMICmdCmdGdbSet::OptionFnDisassemblyFlavor}, + {"fallback", &CMICmdCmdGdbSet::OptionFnFallback}, + {"breakpoint", &CMICmdCmdGdbSet::OptionFnBreakpoint}}; + +//++ +// Details: CMICmdCmdGdbSet constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdGdbSet::CMICmdCmdGdbSet() + : m_constStrArgNamedGdbOption("option"), m_bGdbOptionRecognised(true), + m_bGdbOptionFnSuccessful(false), m_bGbbOptionFnHasError(false), + m_strGdbOptionFnError(MIRSRC(IDS_WORD_ERR_MSG_NOT_IMPLEMENTED_BRKTS)) { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "gdb-set"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdGdbSet::CreateSelf; +} + +//++ +// Details: CMICmdCmdGdbSet destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdGdbSet::~CMICmdCmdGdbSet() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdGdbSet::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValListOfN( + m_constStrArgNamedGdbOption, true, true, + CMICmdArgValListBase::eArgValType_StringAnything)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command is executed in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdGdbSet::Execute() { + CMICMDBASE_GETOPTION(pArgGdbOption, ListOfN, m_constStrArgNamedGdbOption); + const CMICmdArgValListBase::VecArgObjPtr_t &rVecWords( + pArgGdbOption->GetExpectedOptions()); + + // Get the gdb-set option to carry out. This option will be used as an action + // which should be done. Further arguments will be used as parameters for it. + CMICmdArgValListBase::VecArgObjPtr_t::const_iterator it = rVecWords.begin(); + const CMICmdArgValString *pOption = + static_cast(*it); + const CMIUtilString strOption(pOption->GetValue()); + ++it; + + // Retrieve the parameter(s) for the option + CMIUtilString::VecString_t vecWords; + while (it != rVecWords.end()) { + const CMICmdArgValString *pWord = + static_cast(*it); + vecWords.push_back(pWord->GetValue()); + + // Next + ++it; + } + + FnGdbOptionPtr pPrintRequestFn = nullptr; + if (!GetOptionFn(strOption, pPrintRequestFn)) { + // For unimplemented option handlers, fallback on a generic handler + // ToDo: Remove this when ALL options have been implemented + if (!GetOptionFn("fallback", pPrintRequestFn)) { + m_bGdbOptionRecognised = false; + m_strGdbOptionName = "fallback"; // This would be the strOption name + return MIstatus::success; + } + } + + m_bGdbOptionFnSuccessful = (this->*(pPrintRequestFn))(vecWords); + if (!m_bGdbOptionFnSuccessful && !m_bGbbOptionFnHasError) + return MIstatus::failure; + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute() method. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdGdbSet::Acknowledge() { + // Print error if option isn't recognized: + // ^error,msg="The request '%s' was not recognized, not implemented" + if (!m_bGdbOptionRecognised) { + const CMICmnMIValueConst miValueConst( + CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INFO_PRINTFN_NOT_FOUND), + m_strGdbOptionName.c_str())); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + + // ^done,value="%s" + if (m_bGdbOptionFnSuccessful) { + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + + // Print error if request failed: + // ^error,msg="The request '%s' failed. + const CMICmnMIValueConst miValueConst(CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_INFO_PRINTFN_FAILED), m_strGdbOptionFnError.c_str())); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdGdbSet::CreateSelf() { return new CMICmdCmdGdbSet(); } + +//++ +// Details: Retrieve the print function's pointer for the matching print +// request. +// Type: Method. +// Args: vrPrintFnName - (R) The info requested. +// vrwpFn - (W) The print function's pointer of the function +// to carry out +// Return: bool - True = Print request is implemented, false = not found. +// Throws: None. +//-- +bool CMICmdCmdGdbSet::GetOptionFn(const CMIUtilString &vrPrintFnName, + FnGdbOptionPtr &vrwpFn) const { + vrwpFn = nullptr; + + const MapGdbOptionNameToFnGdbOptionPtr_t::const_iterator it = + ms_mapGdbOptionNameToFnGdbOptionPtr.find(vrPrintFnName); + if (it != ms_mapGdbOptionNameToFnGdbOptionPtr.end()) { + vrwpFn = (*it).second; + return true; + } + + return false; +} + +//++ +// Details: Carry out work to complete the GDB set option 'target-async' to +// prepare +// and send back information asked for. +// Type: Method. +// Args: vrWords - (R) List of additional parameters used by this option. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdGdbSet::OptionFnTargetAsync( + const CMIUtilString::VecString_t &vrWords) { + bool bAsyncMode = false; + bool bOk = true; + + if (vrWords.size() > 1) + // Too many arguments. + bOk = false; + else if (vrWords.size() == 0) + // If no arguments, default is "on". + bAsyncMode = true; + else if (CMIUtilString::Compare(vrWords[0], "on")) + bAsyncMode = true; + else if (CMIUtilString::Compare(vrWords[0], "off")) + bAsyncMode = false; + else + // Unrecognized argument. + bOk = false; + + if (!bOk) { + // Report error. + m_bGbbOptionFnHasError = true; + m_strGdbOptionFnError = MIRSRC(IDS_CMD_ERR_GDBSET_OPT_TARGETASYNC); + return MIstatus::failure; + } + + // Turn async mode on/off. + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + rSessionInfo.GetDebugger().SetAsync(bAsyncMode); + + return MIstatus::success; +} + +//++ +// Details: Carry out work to complete the GDB set option +// 'print-char-array-as-string' to +// prepare and send back information asked for. +// Type: Method. +// Args: vrWords - (R) List of additional parameters used by this option. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdGdbSet::OptionFnPrint(const CMIUtilString::VecString_t &vrWords) { + const bool bAllArgs(vrWords.size() == 2); + const bool bArgOn(bAllArgs && (CMIUtilString::Compare(vrWords[1], "on") || + CMIUtilString::Compare(vrWords[1], "1"))); + const bool bArgOff(bAllArgs && (CMIUtilString::Compare(vrWords[1], "off") || + CMIUtilString::Compare(vrWords[1], "0"))); + if (!bAllArgs || (!bArgOn && !bArgOff)) { + m_bGbbOptionFnHasError = true; + m_strGdbOptionFnError = MIRSRC(IDS_CMD_ERR_GDBSET_OPT_PRINT_BAD_ARGS); + return MIstatus::failure; + } + + const CMIUtilString strOption(vrWords[0]); + CMIUtilString strOptionKey; + if (CMIUtilString::Compare(strOption, "char-array-as-string")) + strOptionKey = m_rLLDBDebugSessionInfo.m_constStrPrintCharArrayAsString; + else if (CMIUtilString::Compare(strOption, "expand-aggregates")) + strOptionKey = m_rLLDBDebugSessionInfo.m_constStrPrintExpandAggregates; + else if (CMIUtilString::Compare(strOption, "aggregate-field-names")) + strOptionKey = m_rLLDBDebugSessionInfo.m_constStrPrintAggregateFieldNames; + else { + m_bGbbOptionFnHasError = true; + m_strGdbOptionFnError = CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_GDBSET_OPT_PRINT_UNKNOWN_OPTION), strOption.c_str()); + return MIstatus::failure; + } + + const bool bOptionValue(bArgOn); + if (!m_rLLDBDebugSessionInfo.SharedDataAdd(strOptionKey, + bOptionValue)) { + m_bGbbOptionFnHasError = false; + SetError(CMIUtilString::Format(MIRSRC(IDS_DBGSESSION_ERR_SHARED_DATA_ADD), + m_cmdData.strMiCmd.c_str(), + strOptionKey.c_str())); + return MIstatus::failure; + } + + return MIstatus::success; +} + +//++ +// Details: Carry out work to complete the GDB set option 'solib-search-path' to +// prepare +// and send back information asked for. +// Type: Method. +// Args: vrWords - (R) List of additional parameters used by this option. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdGdbSet::OptionFnSolibSearchPath( + const CMIUtilString::VecString_t &vrWords) { + // Check we have at least one argument + if (vrWords.size() < 1) { + m_bGbbOptionFnHasError = true; + m_strGdbOptionFnError = MIRSRC(IDS_CMD_ERR_GDBSET_OPT_SOLIBSEARCHPATH); + return MIstatus::failure; + } + const CMIUtilString &rStrValSolibPath(vrWords[0]); + + // Add 'solib-search-path' to the shared data list + const CMIUtilString &rStrKeySolibPath( + m_rLLDBDebugSessionInfo.m_constStrSharedDataSolibPath); + if (!m_rLLDBDebugSessionInfo.SharedDataAdd(rStrKeySolibPath, + rStrValSolibPath)) { + m_bGbbOptionFnHasError = false; + SetError(CMIUtilString::Format(MIRSRC(IDS_DBGSESSION_ERR_SHARED_DATA_ADD), + m_cmdData.strMiCmd.c_str(), + rStrKeySolibPath.c_str())); + return MIstatus::failure; + } + + return MIstatus::success; +} + +//++ +// Details: Carry out work to complete the GDB set option 'output-radix' to +// prepare +// and send back information asked for. +// Type: Method. +// Args: vrWords - (R) List of additional parameters used by this option. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdGdbSet::OptionFnOutputRadix( + const CMIUtilString::VecString_t &vrWords) { + // Check we have at least one argument + if (vrWords.size() < 1) { + m_bGbbOptionFnHasError = true; + m_strGdbOptionFnError = MIRSRC(IDS_CMD_ERR_GDBSET_OPT_SOLIBSEARCHPATH); + return MIstatus::failure; + } + const CMIUtilString &rStrValOutputRadix(vrWords[0]); + + CMICmnLLDBDebugSessionInfoVarObj::varFormat_e format = + CMICmnLLDBDebugSessionInfoVarObj::eVarFormat_Invalid; + MIint64 radix; + if (rStrValOutputRadix.ExtractNumber(radix)) { + switch (radix) { + case 8: + format = CMICmnLLDBDebugSessionInfoVarObj::eVarFormat_Octal; + break; + case 10: + format = CMICmnLLDBDebugSessionInfoVarObj::eVarFormat_Natural; + break; + case 16: + format = CMICmnLLDBDebugSessionInfoVarObj::eVarFormat_Hex; + break; + default: + format = CMICmnLLDBDebugSessionInfoVarObj::eVarFormat_Invalid; + break; + } + } + if (format == CMICmnLLDBDebugSessionInfoVarObj::eVarFormat_Invalid) { + m_bGbbOptionFnHasError = false; + SetError(CMIUtilString::Format(MIRSRC(IDS_DBGSESSION_ERR_SHARED_DATA_ADD), + m_cmdData.strMiCmd.c_str(), "Output Radix")); + return MIstatus::failure; + } + CMICmnLLDBDebugSessionInfoVarObj::VarObjSetFormat(format); + + return MIstatus::success; +} + +//++ +// Details: Carry out work to complete the GDB set option 'disassembly-flavor' +// to prepare +// and send back information asked for. +// Type: Method. +// Args: vrWords - (R) List of additional parameters used by this option. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdGdbSet::OptionFnDisassemblyFlavor( + const CMIUtilString::VecString_t &vrWords) { + // Check we have at least one argument + if (vrWords.size() < 1) { + m_bGbbOptionFnHasError = true; + // m_strGdbOptionFnError = MIRSRC(IDS_CMD_ERR_GDBSET_OPT_SOLIBSEARCHPATH); + return MIstatus::failure; + } + const CMIUtilString &rStrValDisasmFlavor(vrWords[0]); + + lldb::SBDebugger &rDbgr = m_rLLDBDebugSessionInfo.GetDebugger(); + lldb::SBError error = lldb::SBDebugger::SetInternalVariable( + "target.x86-disassembly-flavor", rStrValDisasmFlavor.c_str(), + rDbgr.GetInstanceName()); + if (error.Fail()) { + m_strGdbOptionFnError = error.GetCString(); + return MIstatus::failure; + } + + return MIstatus::success; +} + +//++ +// Details: Carry out work to complete the GDB set option 'breakpoint' to +// prepare +// and send back information asked for. +// Type: Method. +// Args: vrWords - (R) List of additional parameters used by this option. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdGdbSet::OptionFnBreakpoint( + const CMIUtilString::VecString_t &vrWords) { + bool bPending = false; + bool bOk = true; + + if (vrWords.size() != 2) + // Wrong number of arguments. + bOk = false; + else if (CMIUtilString::Compare(vrWords[0], "pending") && + (CMIUtilString::Compare(vrWords[1], "on") || + CMIUtilString::Compare(vrWords[1], "1"))) + bPending = true; + else if (CMIUtilString::Compare(vrWords[0], "pending") && + (CMIUtilString::Compare(vrWords[1], "off") || + CMIUtilString::Compare(vrWords[1], "0"))) + bPending = false; + else + // Unrecognized argument(s). + bOk = false; + + if (!bOk) { + // Report error. + m_bGbbOptionFnHasError = false; + SetError(MIRSRC(IDS_CMD_ERR_GDBSET_OPT_BREAKPOINT)); + return MIstatus::failure; + } + + CMIUtilString sPendingVal = bPending ? "on" : "off"; + CMIUtilString sKey = "breakpoint.pending"; + if (!m_rLLDBDebugSessionInfo.SharedDataAdd(sKey, sPendingVal)) { + m_bGbbOptionFnHasError = false; + SetError(CMIUtilString::Format(MIRSRC(IDS_DBGSESSION_ERR_SHARED_DATA_ADD), + m_cmdData.strMiCmd.c_str(), sKey.c_str())); + return MIstatus::failure; + } + + return MIstatus::success; +} + +//++ +// Details: Carry out work to complete the GDB set option to prepare and send +// back the +// requested information. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdGdbSet::OptionFnFallback( + const CMIUtilString::VecString_t &vrWords) { + MIunused(vrWords); + + // Do nothing - intentional. This is a fallback function to do nothing. + // This allows the search for gdb-set options to always succeed when the + // option is not + // found (implemented). + + return MIstatus::success; +} diff --git a/src/MICmdCmdGdbSet.h b/src/MICmdCmdGdbSet.h new file mode 100644 index 00000000000..c88f9976ef6 --- /dev/null +++ b/src/MICmdCmdGdbSet.h @@ -0,0 +1,101 @@ +//===-- MICmdCmdGdbSet.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdGdbSet interface. +// +// To implement new MI commands, derive a new command class from +// the command base +// class. To enable the new command for interpretation add the new +// command class +// to the command factory. The files of relevance are: +// MICmdCommands.cpp +// MICmdBase.h / .cpp +// MICmdCmd.h / .cpp +// For an introduction to adding a new command see +// CMICmdCmdSupportInfoMiCmdQuery +// command class as an example. + +#pragma once + +// In-house headers: +#include "MICmdBase.h" + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "gdb-set". +// This command does not follow the MI documentation exactly. While +// *this +// command is implemented it does not do anything with the gdb-set +// variable past in. +// The design of matching the info request to a request action (or +// command) is very simple. The request function which carries out +// the task of information gathering and printing to stdout is part of +// *this class. Should the request function become more complicated +// then +// that request should really reside in a command type class. Then this +// class instantiates a request info command for a matching request. +// The +// design/code of *this class then does not then become bloated. Use a +// lightweight version of the current MI command system. +//-- +class CMICmdCmdGdbSet : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdGdbSet(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdGdbSet() override; + + // Typedefs: +private: + typedef bool (CMICmdCmdGdbSet::*FnGdbOptionPtr)( + const CMIUtilString::VecString_t &vrWords); + typedef std::map + MapGdbOptionNameToFnGdbOptionPtr_t; + + // Methods: +private: + bool GetOptionFn(const CMIUtilString &vrGdbOptionName, + FnGdbOptionPtr &vrwpFn) const; + bool OptionFnTargetAsync(const CMIUtilString::VecString_t &vrWords); + bool OptionFnPrint(const CMIUtilString::VecString_t &vrWords); + bool OptionFnSolibSearchPath(const CMIUtilString::VecString_t &vrWords); + bool OptionFnOutputRadix(const CMIUtilString::VecString_t &vrWords); + bool OptionFnDisassemblyFlavor(const CMIUtilString::VecString_t &vrWords); + bool OptionFnBreakpoint(const CMIUtilString::VecString_t &vrWords); + bool OptionFnFallback(const CMIUtilString::VecString_t &vrWords); + + // Attributes: +private: + const static MapGdbOptionNameToFnGdbOptionPtr_t + ms_mapGdbOptionNameToFnGdbOptionPtr; + // + const CMIUtilString m_constStrArgNamedGdbOption; + bool m_bGdbOptionRecognised; // True = This command has a function with a name + // that matches the Print argument, false = not + // found + bool m_bGdbOptionFnSuccessful; // True = The print function completed its task + // ok, false = function failed for some reason + bool m_bGbbOptionFnHasError; // True = The option function has an error + // condition (not the command!), false = option + // function ok. + CMIUtilString m_strGdbOptionName; + CMIUtilString m_strGdbOptionFnError; +}; diff --git a/src/MICmdCmdGdbShow.cpp b/src/MICmdCmdGdbShow.cpp new file mode 100644 index 00000000000..9ebd36202b7 --- /dev/null +++ b/src/MICmdCmdGdbShow.cpp @@ -0,0 +1,395 @@ +//===-- MICmdCmdGdbShow.cpp -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdGdbShow implementation. + +// Third party headers: +#include "lldb/API/SBCompileUnit.h" +#include "lldb/API/SBFrame.h" +#include "lldb/API/SBLanguageRuntime.h" +#include "lldb/API/SBStringList.h" +#include "lldb/API/SBThread.h" + +// In-house headers: +#include "MICmdArgValListOfN.h" +#include "MICmdArgValOptionLong.h" +#include "MICmdArgValString.h" +#include "MICmdCmdGdbShow.h" +#include "MICmnLLDBDebugSessionInfo.h" +#include "MICmnMIResultRecord.h" +#include "MICmnMIValueConst.h" + +// Instantiations: +const CMICmdCmdGdbShow::MapGdbOptionNameToFnGdbOptionPtr_t + CMICmdCmdGdbShow::ms_mapGdbOptionNameToFnGdbOptionPtr = { + {"target-async", &CMICmdCmdGdbShow::OptionFnTargetAsync}, + {"print", &CMICmdCmdGdbShow::OptionFnPrint}, + {"language", &CMICmdCmdGdbShow::OptionFnLanguage}, + {"disassembly-flavor", &CMICmdCmdGdbShow::OptionFnDisassemblyFlavor}, + {"fallback", &CMICmdCmdGdbShow::OptionFnFallback}, + {"breakpoint", &CMICmdCmdGdbShow::OptionFnBreakpoint}}; + +//++ +// Details: CMICmdCmdGdbShow constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdGdbShow::CMICmdCmdGdbShow() + : m_constStrArgNamedGdbOption("option"), m_bGdbOptionRecognised(true), + m_bGdbOptionFnSuccessful(false), m_bGbbOptionFnHasError(false), + m_strGdbOptionFnError(MIRSRC(IDS_WORD_ERR_MSG_NOT_IMPLEMENTED_BRKTS)) { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "gdb-show"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdGdbShow::CreateSelf; +} + +//++ +// Details: CMICmdCmdGdbShow destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdGdbShow::~CMICmdCmdGdbShow() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdGdbShow::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValListOfN( + m_constStrArgNamedGdbOption, true, true, + CMICmdArgValListBase::eArgValType_StringAnything)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command is executed in this +// function. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdGdbShow::Execute() { + CMICMDBASE_GETOPTION(pArgGdbOption, ListOfN, m_constStrArgNamedGdbOption); + const CMICmdArgValListBase::VecArgObjPtr_t &rVecWords( + pArgGdbOption->GetExpectedOptions()); + + // Get the gdb-show option to carry out. This option will be used as an action + // which should be done. Further arguments will be used as parameters for it. + CMICmdArgValListBase::VecArgObjPtr_t::const_iterator it = rVecWords.begin(); + const CMICmdArgValString *pOption = + static_cast(*it); + const CMIUtilString strOption(pOption->GetValue()); + ++it; + + // Retrieve the parameter(s) for the option + CMIUtilString::VecString_t vecWords; + while (it != rVecWords.end()) { + const CMICmdArgValString *pWord = + static_cast(*it); + vecWords.push_back(pWord->GetValue()); + + // Next + ++it; + } + + FnGdbOptionPtr pPrintRequestFn = nullptr; + if (!GetOptionFn(strOption, pPrintRequestFn)) { + // For unimplemented option handlers, fallback to a generic handler + // ToDo: Remove this when ALL options have been implemented + if (!GetOptionFn("fallback", pPrintRequestFn)) { + m_bGdbOptionRecognised = false; + m_strGdbOptionName = "fallback"; // This would be the strOption name + return MIstatus::success; + } + } + + m_bGdbOptionFnSuccessful = (this->*(pPrintRequestFn))(vecWords); + if (!m_bGdbOptionFnSuccessful && !m_bGbbOptionFnHasError) + return MIstatus::failure; + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute() method. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdGdbShow::Acknowledge() { + // Print error if option isn't recognized: + // ^error,msg="The request '%s' was not recognized, not implemented" + if (!m_bGdbOptionRecognised) { + const CMICmnMIValueConst miValueConst( + CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INFO_PRINTFN_NOT_FOUND), + m_strGdbOptionName.c_str())); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + + // ^done,value="%s" + if (m_bGdbOptionFnSuccessful && !m_strValue.empty()) { + const CMICmnMIValueConst miValueConst(m_strValue); + const CMICmnMIValueResult miValueResult("value", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } else if (m_bGdbOptionFnSuccessful) { + // Ignore empty value (for fallback) + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + + // Print error if request failed: + // ^error,msg="The request '%s' failed. + const CMICmnMIValueConst miValueConst(CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_INFO_PRINTFN_FAILED), m_strGdbOptionFnError.c_str())); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdGdbShow::CreateSelf() { return new CMICmdCmdGdbShow(); } + +//++ +// Details: Retrieve the print function's pointer for the matching print +// request. +// Type: Method. +// Args: vrPrintFnName - (R) The info requested. +// vrwpFn - (W) The print function's pointer of the function +// to carry out +// Return: bool - True = Print request is implemented, false = not found. +// Throws: None. +//-- +bool CMICmdCmdGdbShow::GetOptionFn(const CMIUtilString &vrPrintFnName, + FnGdbOptionPtr &vrwpFn) const { + vrwpFn = nullptr; + + const MapGdbOptionNameToFnGdbOptionPtr_t::const_iterator it = + ms_mapGdbOptionNameToFnGdbOptionPtr.find(vrPrintFnName); + if (it != ms_mapGdbOptionNameToFnGdbOptionPtr.end()) { + vrwpFn = (*it).second; + return true; + } + + return false; +} + +//++ +// Details: Carry out work to complete the GDB show option 'target-async' to +// prepare +// and send back the requested information. +// Type: Method. +// Args: vrWords - (R) List of additional parameters used by this option. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdGdbShow::OptionFnTargetAsync( + const CMIUtilString::VecString_t &vrWords) { + MIunused(vrWords); + + // Get async mode + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + const bool bAsyncMode = rSessionInfo.GetDebugger().GetAsync(); + + m_strValue = bAsyncMode ? "on" : "off"; + return MIstatus::success; +} + +//++ +// Details: Carry out work to complete the GDB show option 'print' to prepare +// and send +// back the requested information. +// Type: Method. +// Args: vrWords - (R) List of additional parameters used by this option. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdGdbShow::OptionFnPrint( + const CMIUtilString::VecString_t &vrWords) { + const bool bAllArgs(vrWords.size() == 1); + if (!bAllArgs) { + m_bGbbOptionFnHasError = true; + m_strGdbOptionFnError = MIRSRC(IDS_CMD_ERR_GDBSHOW_OPT_PRINT_BAD_ARGS); + return MIstatus::failure; + } + + const CMIUtilString strOption(vrWords[0]); + CMIUtilString strOptionKey; + bool bOptionValueDefault = false; + if (CMIUtilString::Compare(strOption, "char-array-as-string")) + strOptionKey = m_rLLDBDebugSessionInfo.m_constStrPrintCharArrayAsString; + else if (CMIUtilString::Compare(strOption, "expand-aggregates")) + strOptionKey = m_rLLDBDebugSessionInfo.m_constStrPrintExpandAggregates; + else if (CMIUtilString::Compare(strOption, "aggregate-field-names")) { + strOptionKey = m_rLLDBDebugSessionInfo.m_constStrPrintAggregateFieldNames; + bOptionValueDefault = true; + } else { + m_bGbbOptionFnHasError = true; + m_strGdbOptionFnError = CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_GDBSHOW_OPT_PRINT_UNKNOWN_OPTION), + strOption.c_str()); + return MIstatus::failure; + } + + bool bOptionValue = false; + bOptionValue = bOptionValueDefault + ? !m_rLLDBDebugSessionInfo.SharedDataRetrieve( + strOptionKey, bOptionValue) || + bOptionValue + : m_rLLDBDebugSessionInfo.SharedDataRetrieve( + strOptionKey, bOptionValue) && + bOptionValue; + + m_strValue = bOptionValue ? "on" : "off"; + return MIstatus::success; +} + +//++ +// Details: Carry out work to complete the GDB show option 'language' to prepare +// and send back the requested information. +// Type: Method. +// Args: vrWords - (R) List of additional parameters used by this option. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdGdbShow::OptionFnLanguage( + const CMIUtilString::VecString_t &vrWords) { + MIunused(vrWords); + + // Get current language + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBThread sbThread = rSessionInfo.GetProcess().GetSelectedThread(); + const lldb::SBFrame sbFrame = sbThread.GetSelectedFrame(); + lldb::SBCompileUnit sbCompileUnit = sbFrame.GetCompileUnit(); + const lldb::LanguageType eLanguageType = sbCompileUnit.GetLanguage(); + + m_strValue = lldb::SBLanguageRuntime::GetNameForLanguageType(eLanguageType); + return MIstatus::success; +} + +//++ +// Details: Carry out work to complete the GDB show option 'disassembly-flavor' to prepare +// and send back the requested information. +// Type: Method. +// Args: vrWords - (R) List of additional parameters used by this option. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdGdbShow::OptionFnDisassemblyFlavor(const CMIUtilString::VecString_t &vrWords) { + MIunused(vrWords); + + // Get current disassembly flavor + lldb::SBDebugger &rDbgr = m_rLLDBDebugSessionInfo.GetDebugger(); + m_strValue = lldb::SBDebugger::GetInternalVariableValue("target.x86-disassembly-flavor", + rDbgr.GetInstanceName()).GetStringAtIndex(0); + return MIstatus::success; +} + +//++ +// Details: Carry out work to complete the GDB show option 'breakpoint' to +// prepare +// and send back the requested information. +// Type: Method. +// Args: vrWords - (R) List of additional parameters used by this option. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdGdbShow::OptionFnBreakpoint( + const CMIUtilString::VecString_t &vrWords) { + if (vrWords.size() != 1) { + m_bGbbOptionFnHasError = true; + m_strGdbOptionFnError = MIRSRC(IDS_CMD_ERR_GDBSHOW_OPT_BREAKPOINT_BAD_ARGS); + return MIstatus::failure; + } + + const CMIUtilString strOption(vrWords[0]); + if (!CMIUtilString::Compare(strOption, "pending")) { + m_bGbbOptionFnHasError = true; + m_strGdbOptionFnError = CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_GDBSHOW_OPT_BREAKPOINT_UNKNOWN_OPTION), + strOption.c_str()); + return MIstatus::failure; + } + + if (!m_rLLDBDebugSessionInfo.SharedDataRetrieve("breakpoint.pending", + m_strValue)) { + if (m_strValue.empty()) + m_strValue = "off"; + } + + return MIstatus::success; +} + +//++ +// Details: Carry out work to complete the GDB show option to prepare and send +// back the +// requested information. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdGdbShow::OptionFnFallback( + const CMIUtilString::VecString_t &vrWords) { + MIunused(vrWords); + + // Do nothing - intentional. This is a fallback function to do nothing. + // This allows the search for gdb-show options to always succeed when the + // option is not + // found (implemented). + + return MIstatus::success; +} diff --git a/src/MICmdCmdGdbShow.h b/src/MICmdCmdGdbShow.h new file mode 100644 index 00000000000..9de034c617c --- /dev/null +++ b/src/MICmdCmdGdbShow.h @@ -0,0 +1,101 @@ +//===-- MICmdCmdGdbShow.h ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdGdbShow interface. +// +// To implement new MI commands, derive a new command class from +// the command base +// class. To enable the new command for interpretation add the new +// command class +// to the command factory. The files of relevance are: +// MICmdCommands.cpp +// MICmdBase.h / .cpp +// MICmdCmd.h / .cpp +// For an introduction to adding a new command see +// CMICmdCmdSupportInfoMiCmdQuery +// command class as an example. + +#pragma once + +// In-house headers: +#include "MICmdBase.h" + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "gdb-show". +// This command does not follow the MI documentation exactly. While +// *this +// command is implemented it does not do anything with the gdb-set +// variable past in. +// The design of matching the info request to a request action (or +// command) is very simple. The request function which carries out +// the task of information gathering and printing to stdout is part of +// *this class. Should the request function become more complicated +// then +// that request should really reside in a command type class. Then this +// class instantiates a request info command for a matching request. +// The +// design/code of *this class then does not then become bloated. Use a +// lightweight version of the current MI command system. +//-- +class CMICmdCmdGdbShow : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdGdbShow(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdGdbShow() override; + + // Typedefs: +private: + typedef bool (CMICmdCmdGdbShow::*FnGdbOptionPtr)( + const CMIUtilString::VecString_t &vrWords); + typedef std::map + MapGdbOptionNameToFnGdbOptionPtr_t; + + // Methods: +private: + bool GetOptionFn(const CMIUtilString &vrGdbOptionName, + FnGdbOptionPtr &vrwpFn) const; + bool OptionFnTargetAsync(const CMIUtilString::VecString_t &vrWords); + bool OptionFnPrint(const CMIUtilString::VecString_t &vrWords); + bool OptionFnLanguage(const CMIUtilString::VecString_t &vrWords); + bool OptionFnDisassemblyFlavor(const CMIUtilString::VecString_t &vrWords); + bool OptionFnFallback(const CMIUtilString::VecString_t &vrWords); + bool OptionFnBreakpoint(const CMIUtilString::VecString_t &vrWords); + + // Attributes: +private: + const static MapGdbOptionNameToFnGdbOptionPtr_t + ms_mapGdbOptionNameToFnGdbOptionPtr; + + const CMIUtilString m_constStrArgNamedGdbOption; + bool m_bGdbOptionRecognised; // True = This command has a function with a name + // that matches the Print argument, false = not + // found + bool m_bGdbOptionFnSuccessful; // True = The print function completed its task + // ok, false = function failed for some reason + bool m_bGbbOptionFnHasError; // True = The option function has an error + // condition (not the command!), false = option + // function ok. + CMIUtilString m_strGdbOptionName; + CMIUtilString m_strGdbOptionFnError; + CMIUtilString m_strValue; +}; diff --git a/src/MICmdCmdGdbThread.cpp b/src/MICmdCmdGdbThread.cpp new file mode 100644 index 00000000000..132cf2c68ef --- /dev/null +++ b/src/MICmdCmdGdbThread.cpp @@ -0,0 +1,89 @@ +//===-- MICmdCmdGdbThread.cpp -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdGdbThread implementation. + +// In-house headers: +#include "MICmdCmdGdbThread.h" +#include "MICmnMIResultRecord.h" +#include "MICmnMIValueConst.h" + +//++ +// Details: CMICmdCmdGdbThread constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdGdbThread::CMICmdCmdGdbThread() { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "thread"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdGdbThread::CreateSelf; +} + +//++ +// Details: CMICmdCmdThread destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdGdbThread::~CMICmdCmdGdbThread() {} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdGdbThread::Execute() { + // Do nothing + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdGdbThread::Acknowledge() { + const CMICmnMIValueConst miValueConst(MIRSRC(IDS_WORD_NOT_IMPLEMENTED)); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdGdbThread::CreateSelf() { + return new CMICmdCmdGdbThread(); +} diff --git a/src/MICmdCmdGdbThread.h b/src/MICmdCmdGdbThread.h new file mode 100644 index 00000000000..963001f0191 --- /dev/null +++ b/src/MICmdCmdGdbThread.h @@ -0,0 +1,50 @@ +//===-- MICmdCmdGdbThread.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdGdbThread interface. +// +// To implement new MI commands derive a new command class from the +// command base +// class. To enable the new command for interpretation add the new +// command class +// to the command factory. The files of relevance are: +// MICmdCommands.cpp +// MICmdBase.h / .cpp +// MICmdCmd.h / .cpp +// For an introduction to adding a new command see +// CMICmdCmdSupportInfoMiCmdQuery +// command class as an example. + +#pragma once + +// In-house headers: +#include "MICmdBase.h" + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements GDB command "thread". +//-- +class CMICmdCmdGdbThread : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdGdbThread(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdGdbThread() override; +}; diff --git a/src/MICmdCmdMiscellanous.cpp b/src/MICmdCmdMiscellanous.cpp new file mode 100644 index 00000000000..5aa79599953 --- /dev/null +++ b/src/MICmdCmdMiscellanous.cpp @@ -0,0 +1,586 @@ +//===-- MICmdCmdMiscellanous.cpp --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdGdbExit implementation. +// CMICmdCmdListThreadGroups implementation. +// CMICmdCmdInterpreterExec implementation. +// CMICmdCmdInferiorTtySet implementation. + +// Third Party Headers: +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBThread.h" + +// In-house headers: +#include "MICmdArgValFile.h" +#include "MICmdArgValListOfN.h" +#include "MICmdArgValNumber.h" +#include "MICmdArgValOptionLong.h" +#include "MICmdArgValOptionShort.h" +#include "MICmdArgValString.h" +#include "MICmdArgValThreadGrp.h" +#include "MICmdCmdMiscellanous.h" +#include "MICmnLLDBDebugSessionInfo.h" +#include "MICmnLLDBDebugger.h" +#include "MICmnMIOutOfBandRecord.h" +#include "MICmnMIResultRecord.h" +#include "MICmnMIValueConst.h" +#include "MICmnStreamStderr.h" +#include "MICmnStreamStdout.h" +#include "MIDriverBase.h" + +//++ +// Details: CMICmdCmdGdbExit constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdGdbExit::CMICmdCmdGdbExit() { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "gdb-exit"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdGdbExit::CreateSelf; +} + +//++ +// Details: CMICmdCmdGdbExit destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdGdbExit::~CMICmdCmdGdbExit() {} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdGdbExit::Execute() { + CMICmnLLDBDebugger::Instance().GetDriver().SetExitApplicationFlag(true); + const lldb::SBError sbErr = m_rLLDBDebugSessionInfo.GetProcess().Destroy(); + // Do not check for sbErr.Fail() here, m_lldbProcess is likely !IsValid() + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdGdbExit::Acknowledge() { + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Exit); + m_miResultRecord = miRecordResult; + + // Prod the client i.e. Eclipse with out-of-band results to help it 'continue' + // because it is using LLDB debugger + // Give the client '=thread-group-exited,id="i1"' + m_bHasResultRecordExtra = true; + const CMICmnMIValueConst miValueConst2("i1"); + const CMICmnMIValueResult miValueResult2("id", miValueConst2); + const CMICmnMIOutOfBandRecord miOutOfBand( + CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupExited, miValueResult2); + m_miResultRecordExtra = miOutOfBand.GetString(); + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdGdbExit::CreateSelf() { return new CMICmdCmdGdbExit(); } + + +//++ +// Details: CMICmdCmdListThreadGroups constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdListThreadGroups::CMICmdCmdListThreadGroups() + : m_bIsI1(false), m_bHaveArgOption(false), m_bHaveArgRecurse(false), + m_constStrArgNamedAvailable("available"), + m_constStrArgNamedRecurse("recurse"), m_constStrArgNamedGroup("group"), + m_constStrArgNamedThreadGroup("i1") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "list-thread-groups"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdListThreadGroups::CreateSelf; +} + +//++ +// Details: CMICmdCmdListThreadGroups destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdListThreadGroups::~CMICmdCmdListThreadGroups() { + m_vecMIValueTuple.clear(); +} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdListThreadGroups::ParseArgs() { + m_setCmdArgs.Add( + new CMICmdArgValOptionLong(m_constStrArgNamedAvailable, false, true)); + m_setCmdArgs.Add( + new CMICmdArgValOptionLong(m_constStrArgNamedRecurse, false, true, + CMICmdArgValListBase::eArgValType_Number, 1)); + m_setCmdArgs.Add( + new CMICmdArgValListOfN(m_constStrArgNamedGroup, false, true, + CMICmdArgValListBase::eArgValType_Number)); + m_setCmdArgs.Add( + new CMICmdArgValThreadGrp(m_constStrArgNamedThreadGroup, false, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Synopsis: -list-thread-groups [ --available ] [ --recurse 1 ] [ +// group ... ] +// This command does not follow the MI documentation exactly. Has an +// extra +// argument "i1" to handle. +// Ref: +// http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Miscellaneous-Commands.html#GDB_002fMI-Miscellaneous-Commands +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdListThreadGroups::Execute() { + if (m_setCmdArgs.IsArgContextEmpty()) + // No options so "top level thread groups" + return MIstatus::success; + + CMICMDBASE_GETOPTION(pArgAvailable, OptionLong, m_constStrArgNamedAvailable); + CMICMDBASE_GETOPTION(pArgRecurse, OptionLong, m_constStrArgNamedRecurse); + CMICMDBASE_GETOPTION(pArgThreadGroup, ThreadGrp, + m_constStrArgNamedThreadGroup); + + // Got some options so "threads" + if (pArgAvailable->GetFound()) { + if (pArgRecurse->GetFound()) { + m_bHaveArgRecurse = true; + return MIstatus::success; + } + + m_bHaveArgOption = true; + return MIstatus::success; + } + // "i1" as first argument (pos 0 of possible arg) + if (!pArgThreadGroup->GetFound()) + return MIstatus::success; + m_bIsI1 = true; + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); + + // Note do not check for sbProcess is IsValid(), continue + + m_vecMIValueTuple.clear(); + const MIuint nThreads = sbProcess.GetNumThreads(); + for (MIuint i = 0; i < nThreads; i++) { + // GetThreadAtIndex() uses a base 0 index + // GetThreadByIndexID() uses a base 1 index + lldb::SBThread thread = sbProcess.GetThreadAtIndex(i); + + if (thread.IsValid()) { + CMICmnMIValueTuple miTuple; + if (!rSessionInfo.MIResponseFormThreadInfo( + m_cmdData, thread, + CMICmnLLDBDebugSessionInfo::eThreadInfoFormat_NoFrames, miTuple)) + return MIstatus::failure; + + m_vecMIValueTuple.push_back(miTuple); + } + } + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdListThreadGroups::Acknowledge() { + if (m_bHaveArgOption) { + if (m_bHaveArgRecurse) { + const CMICmnMIValueConst miValueConst( + MIRSRC(IDS_WORD_NOT_IMPLEMENTED_BRKTS)); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; + } + + const CMICmnMIValueConst miValueConst1("i1"); + const CMICmnMIValueResult miValueResult1("id", miValueConst1); + CMICmnMIValueTuple miTuple(miValueResult1); + + const CMICmnMIValueConst miValueConst2("process"); + const CMICmnMIValueResult miValueResult2("type", miValueConst2); + miTuple.Add(miValueResult2); + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + if (rSessionInfo.GetProcess().IsValid()) { + const lldb::pid_t pid = rSessionInfo.GetProcess().GetProcessID(); + const CMIUtilString strPid(CMIUtilString::Format("%lld", pid)); + const CMICmnMIValueConst miValueConst3(strPid); + const CMICmnMIValueResult miValueResult3("pid", miValueConst3); + miTuple.Add(miValueResult3); + } + + const CMICmnMIValueConst miValueConst4( + MIRSRC(IDS_WORD_NOT_IMPLEMENTED_BRKTS)); + const CMICmnMIValueResult miValueResult4("num_children", miValueConst4); + miTuple.Add(miValueResult4); + + const CMICmnMIValueConst miValueConst5( + MIRSRC(IDS_WORD_NOT_IMPLEMENTED_BRKTS)); + const CMICmnMIValueResult miValueResult5("cores", miValueConst5); + miTuple.Add(miValueResult5); + + const CMICmnMIValueList miValueList(miTuple); + const CMICmnMIValueResult miValueResult6("groups", miValueList); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult6); + m_miResultRecord = miRecordResult; + + return MIstatus::success; + } + + if (!m_bIsI1) { + const CMICmnMIValueConst miValueConst1("i1"); + const CMICmnMIValueResult miValueResult1("id", miValueConst1); + CMICmnMIValueTuple miTuple(miValueResult1); + + const CMICmnMIValueConst miValueConst2("process"); + const CMICmnMIValueResult miValueResult2("type", miValueConst2); + miTuple.Add(miValueResult2); + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + if (rSessionInfo.GetProcess().IsValid()) { + const lldb::pid_t pid = rSessionInfo.GetProcess().GetProcessID(); + const CMIUtilString strPid(CMIUtilString::Format("%lld", pid)); + const CMICmnMIValueConst miValueConst3(strPid); + const CMICmnMIValueResult miValueResult3("pid", miValueConst3); + miTuple.Add(miValueResult3); + } + + if (rSessionInfo.GetTarget().IsValid()) { + lldb::SBTarget sbTrgt = rSessionInfo.GetTarget(); + const char *pDir = sbTrgt.GetExecutable().GetDirectory(); + const char *pFileName = sbTrgt.GetExecutable().GetFilename(); + const CMIUtilString strFile( + CMIUtilString::Format("%s/%s", + CMIUtilString::WithNullAsEmpty(pDir), + CMIUtilString::WithNullAsEmpty(pFileName))); + const CMICmnMIValueConst miValueConst4(strFile); + const CMICmnMIValueResult miValueResult4("executable", miValueConst4); + miTuple.Add(miValueResult4); + } + + const CMICmnMIValueList miValueList(miTuple); + const CMICmnMIValueResult miValueResult5("groups", miValueList); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult5); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + + // Build up a list of thread information from tuples + VecMIValueTuple_t::const_iterator it = m_vecMIValueTuple.begin(); + if (it == m_vecMIValueTuple.end()) { + const CMICmnMIValueConst miValueConst("[]"); + const CMICmnMIValueResult miValueResult("threads", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + CMICmnMIValueList miValueList(*it); + ++it; + while (it != m_vecMIValueTuple.end()) { + const CMICmnMIValueTuple &rTuple(*it); + miValueList.Add(rTuple); + + // Next + ++it; + } + + const CMICmnMIValueResult miValueResult("threads", miValueList); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdListThreadGroups::CreateSelf() { + return new CMICmdCmdListThreadGroups(); +} + + +//++ +// Details: CMICmdCmdInterpreterExec constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdInterpreterExec::CMICmdCmdInterpreterExec() + : m_constStrArgNamedInterpreter("interpreter"), + m_constStrArgNamedCommand("command") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "interpreter-exec"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdInterpreterExec::CreateSelf; +} + +//++ +// Details: CMICmdCmdInterpreterExec destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdInterpreterExec::~CMICmdCmdInterpreterExec() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdInterpreterExec::ParseArgs() { + m_setCmdArgs.Add( + new CMICmdArgValString(m_constStrArgNamedInterpreter, true, true)); + m_setCmdArgs.Add( + new CMICmdArgValString(m_constStrArgNamedCommand, true, true, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdInterpreterExec::Execute() { + CMICMDBASE_GETOPTION(pArgInterpreter, String, m_constStrArgNamedInterpreter); + CMICMDBASE_GETOPTION(pArgCommand, String, m_constStrArgNamedCommand); + + // Handle the interpreter parameter by do nothing on purpose (set to 'handled' + // in the arg definition above) + const CMIUtilString &rStrInterpreter(pArgInterpreter->GetValue()); + MIunused(rStrInterpreter); + + const CMIUtilString &rStrCommand(pArgCommand->GetValue()); + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + const lldb::ReturnStatus rtn = + rSessionInfo.GetDebugger().GetCommandInterpreter().HandleCommand( + rStrCommand.c_str(), m_lldbResult, true); + MIunused(rtn); + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdInterpreterExec::Acknowledge() { + if (m_lldbResult.GetOutputSize() > 0) { + const CMIUtilString line(m_lldbResult.GetOutput()); + const bool bEscapeQuotes(true); + CMICmnMIValueConst miValueConst(line.Escape(bEscapeQuotes)); + CMICmnMIOutOfBandRecord miOutOfBandRecord(CMICmnMIOutOfBandRecord::eOutOfBand_ConsoleStreamOutput, miValueConst); + const bool bOk = CMICmnStreamStdout::TextToStdout(miOutOfBandRecord.GetString()); + if (!bOk) + return MIstatus::failure; + } + if (m_lldbResult.GetErrorSize() > 0) { + const CMIUtilString line(m_lldbResult.GetError()); + const bool bEscapeQuotes(true); + CMICmnMIValueConst miValueConst(line.Escape(bEscapeQuotes)); + CMICmnMIOutOfBandRecord miOutOfBandRecord(CMICmnMIOutOfBandRecord::eOutOfBand_LogStreamOutput, miValueConst); + const bool bOk = CMICmnStreamStdout::TextToStdout(miOutOfBandRecord.GetString()); + if (!bOk) + return MIstatus::failure; + } + + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdInterpreterExec::CreateSelf() { + return new CMICmdCmdInterpreterExec(); +} + + +//++ +// Details: CMICmdCmdInferiorTtySet constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdInferiorTtySet::CMICmdCmdInferiorTtySet() { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "inferior-tty-set"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdInferiorTtySet::CreateSelf; +} + +//++ +// Details: CMICmdCmdInferiorTtySet destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdInferiorTtySet::~CMICmdCmdInferiorTtySet() {} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdInferiorTtySet::Execute() { + // Do nothing + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdInferiorTtySet::Acknowledge() { + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdInferiorTtySet::CreateSelf() { + return new CMICmdCmdInferiorTtySet(); +} diff --git a/src/MICmdCmdMiscellanous.h b/src/MICmdCmdMiscellanous.h new file mode 100644 index 00000000000..614638e5934 --- /dev/null +++ b/src/MICmdCmdMiscellanous.h @@ -0,0 +1,156 @@ +//===-- MICmdCmdMiscellanous.h ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdGdbExit interface. +// CMICmdCmdListThreadGroups interface. +// CMICmdCmdInterpreterExec interface. +// CMICmdCmdInferiorTtySet interface. +// +// To implement new MI commands derive a new command class from the +// command base +// class. To enable the new command for interpretation add the new +// command class +// to the command factory. The files of relevance are: +// MICmdCommands.cpp +// MICmdBase.h / .cpp +// MICmdCmd.h / .cpp +// For an introduction to adding a new command see +// CMICmdCmdSupportInfoMiCmdQuery +// command class as an example. + +#pragma once + +// Third party headers: +#include "lldb/API/SBCommandReturnObject.h" + +// In-house headers: +#include "MICmdBase.h" +#include "MICmnMIValueList.h" +#include "MICmnMIValueTuple.h" + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "gdb-exit". +//-- +class CMICmdCmdGdbExit : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdGdbExit(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdGdbExit() override; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "list-thread-groups". +// This command does not follow the MI documentation exactly. +// http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Miscellaneous-Commands.html#GDB_002fMI-Miscellaneous-Commands +//-- +class CMICmdCmdListThreadGroups : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdListThreadGroups(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdListThreadGroups() override; + + // Typedefs: +private: + typedef std::vector VecMIValueTuple_t; + + // Attributes: +private: + bool m_bIsI1; // True = Yes command argument equal "i1", false = no match + bool m_bHaveArgOption; // True = Yes "--available" present, false = not found + bool m_bHaveArgRecurse; // True = Yes command argument "--recurse", false = no + // found + VecMIValueTuple_t m_vecMIValueTuple; + const CMIUtilString m_constStrArgNamedAvailable; + const CMIUtilString m_constStrArgNamedRecurse; + const CMIUtilString m_constStrArgNamedGroup; + const CMIUtilString m_constStrArgNamedThreadGroup; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "interpreter-exec". +//-- +class CMICmdCmdInterpreterExec : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdInterpreterExec(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdInterpreterExec() override; + + // Attributes: +private: + const CMIUtilString m_constStrArgNamedInterpreter; + const CMIUtilString m_constStrArgNamedCommand; + lldb::SBCommandReturnObject m_lldbResult; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "inferior-tty-set". +//-- +class CMICmdCmdInferiorTtySet : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdInferiorTtySet(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdInferiorTtySet() override; +}; diff --git a/src/MICmdCmdStack.cpp b/src/MICmdCmdStack.cpp new file mode 100644 index 00000000000..599db37417f --- /dev/null +++ b/src/MICmdCmdStack.cpp @@ -0,0 +1,1053 @@ +//===-- MICmdCmdStack.cpp ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdStackInfoDepth implementation. +// CMICmdCmdStackInfoFrame implementation. +// CMICmdCmdStackListFrames implementation. +// CMICmdCmdStackListArguments implementation. +// CMICmdCmdStackListLocals implementation. +// CMICmdCmdStackSelectFrame implementation. + +// Third Party Headers: +#include "lldb/API/SBThread.h" + +// In-house headers: +#include "MICmdArgValListOfN.h" +#include "MICmdArgValNumber.h" +#include "MICmdArgValOptionLong.h" +#include "MICmdArgValOptionShort.h" +#include "MICmdArgValPrintValues.h" +#include "MICmdArgValString.h" +#include "MICmdArgValThreadGrp.h" +#include "MICmdCmdStack.h" +#include "MICmnLLDBDebugSessionInfo.h" +#include "MICmnLLDBDebugger.h" +#include "MICmnMIOutOfBandRecord.h" +#include "MICmnMIResultRecord.h" +#include "MICmnMIValueConst.h" + +#include + +//++ +// Details: CMICmdCmdStackInfoDepth constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdStackInfoDepth::CMICmdCmdStackInfoDepth() + : m_nThreadFrames(0), m_constStrArgMaxDepth("max-depth") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "stack-info-depth"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdStackInfoDepth::CreateSelf; +} + +//++ +// Details: CMICmdCmdStackInfoDepth destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdStackInfoDepth::~CMICmdCmdStackInfoDepth() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdStackInfoDepth::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgMaxDepth, false, false)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdStackInfoDepth::Execute() { + CMICMDBASE_GETOPTION(pArgThread, OptionLong, m_constStrArgThread); + CMICMDBASE_GETOPTION(pArgMaxDepth, Number, m_constStrArgMaxDepth); + + // Retrieve the --thread option's thread ID (only 1) + MIuint64 nThreadId = UINT64_MAX; + if (pArgThread->GetFound() && + !pArgThread->GetExpectedOption(nThreadId)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND), + m_cmdData.strMiCmd.c_str(), + m_constStrArgThread.c_str())); + return MIstatus::failure; + } + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); + lldb::SBThread thread = (nThreadId != UINT64_MAX) + ? sbProcess.GetThreadByIndexID(nThreadId) + : sbProcess.GetSelectedThread(); + m_nThreadFrames = thread.GetNumFrames(); + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdStackInfoDepth::Acknowledge() { + const CMIUtilString strDepth(CMIUtilString::Format("%d", m_nThreadFrames)); + const CMICmnMIValueConst miValueConst(strDepth); + const CMICmnMIValueResult miValueResult("depth", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdStackInfoDepth::CreateSelf() { + return new CMICmdCmdStackInfoDepth(); +} + + +//++ +// Details: CMICmdCmdStackInfoFrame constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdStackInfoFrame::CMICmdCmdStackInfoFrame() { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "stack-info-frame"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdStackInfoFrame::CreateSelf; +} + +//++ +// Details: CMICmdCmdStackInfoFrame destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdStackInfoFrame::~CMICmdCmdStackInfoFrame() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdStackInfoFrame::ParseArgs() { return ParseValidateCmdOptions(); } + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdStackInfoFrame::Execute() { + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); + if (!sbProcess.IsValid()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_PROCESS), + m_cmdData.strMiCmd.c_str())); + return MIstatus::failure; + } + + lldb::SBThread sbThread = sbProcess.GetSelectedThread(); + MIuint nFrameId = sbThread.GetSelectedFrame().GetFrameID(); + if (!rSessionInfo.MIResponseFormFrameInfo( + sbThread, nFrameId, + CMICmnLLDBDebugSessionInfo::eFrameInfoFormat_NoArguments, + m_miValueTuple)) + return MIstatus::failure; + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdStackInfoFrame::Acknowledge() { + const CMICmnMIValueResult miValueResult("frame", m_miValueTuple); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdStackInfoFrame::CreateSelf() { + return new CMICmdCmdStackInfoFrame(); +} + + +//++ +// Details: CMICmdCmdStackListFrames constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdStackListFrames::CMICmdCmdStackListFrames() + : m_nThreadFrames(0), m_constStrArgFrameLow("low-frame"), + m_constStrArgFrameHigh("high-frame") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "stack-list-frames"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdStackListFrames::CreateSelf; +} + +//++ +// Details: CMICmdCmdStackListFrames destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdStackListFrames::~CMICmdCmdStackListFrames() { + m_vecMIValueResult.clear(); +} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdStackListFrames::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgFrameLow, false, true)); + m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgFrameHigh, false, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdStackListFrames::Execute() { + CMICMDBASE_GETOPTION(pArgThread, OptionLong, m_constStrArgThread); + CMICMDBASE_GETOPTION(pArgFrameLow, Number, m_constStrArgFrameLow); + CMICMDBASE_GETOPTION(pArgFrameHigh, Number, m_constStrArgFrameHigh); + + // Retrieve the --thread option's thread ID (only 1) + MIuint64 nThreadId = UINT64_MAX; + if (pArgThread->GetFound() && + !pArgThread->GetExpectedOption(nThreadId)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND), + m_cmdData.strMiCmd.c_str(), + m_constStrArgThread.c_str())); + return MIstatus::failure; + } + + // Frame low and high options are not mandatory + MIuint nFrameHigh = + pArgFrameHigh->GetFound() ? pArgFrameHigh->GetValue() : UINT32_MAX; + const MIuint nFrameLow = + pArgFrameLow->GetFound() ? pArgFrameLow->GetValue() : 0; + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); + lldb::SBThread thread = (nThreadId != UINT64_MAX) + ? sbProcess.GetThreadByIndexID(nThreadId) + : sbProcess.GetSelectedThread(); + MIuint nThreadFrames = thread.GetNumFrames(); + + // Adjust nThreadFrames for the nFrameHigh argument as we use nFrameHigh+1 in + // the min calc as the arg + // is not an index, but a frame id value. + if (nFrameHigh < UINT32_MAX) { + nFrameHigh++; + nThreadFrames = (nFrameHigh < nThreadFrames) ? nFrameHigh : nThreadFrames; + } + + m_nThreadFrames = nThreadFrames; + if (nThreadFrames == 0) + return MIstatus::success; + + m_vecMIValueResult.clear(); + for (MIuint nLevel = nFrameLow; nLevel < nThreadFrames; nLevel++) { + CMICmnMIValueTuple miValueTuple; + if (!rSessionInfo.MIResponseFormFrameInfo( + thread, nLevel, + CMICmnLLDBDebugSessionInfo::eFrameInfoFormat_NoArguments, + miValueTuple)) + return MIstatus::failure; + + const CMICmnMIValueResult miValueResult8("frame", miValueTuple); + m_vecMIValueResult.push_back(miValueResult8); + } + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdStackListFrames::Acknowledge() { + if (m_nThreadFrames == 0) { + // MI print "3^done,stack=[{}]" + const CMICmnMIValueTuple miValueTuple; + const CMICmnMIValueList miValueList(miValueTuple); + const CMICmnMIValueResult miValueResult("stack", miValueList); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; + } + + // Build up a list of thread information from tuples + VecMIValueResult_t::const_iterator it = m_vecMIValueResult.begin(); + if (it == m_vecMIValueResult.end()) { + // MI print "3^done,stack=[{}]" + const CMICmnMIValueTuple miValueTuple; + const CMICmnMIValueList miValueList(miValueTuple); + const CMICmnMIValueResult miValueResult("stack", miValueList); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + CMICmnMIValueList miValueList(*it); + ++it; + while (it != m_vecMIValueResult.end()) { + const CMICmnMIValueResult &rTuple(*it); + miValueList.Add(rTuple); + + // Next + ++it; + } + const CMICmnMIValueResult miValueResult("stack", miValueList); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdStackListFrames::CreateSelf() { + return new CMICmdCmdStackListFrames(); +} + + +//++ +// Details: CMICmdCmdStackListArguments constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdStackListArguments::CMICmdCmdStackListArguments() + : m_bThreadInvalid(false), m_miValueList(true), + m_constStrArgPrintValues("print-values"), + m_constStrArgFrameLow("low-frame"), m_constStrArgFrameHigh("high-frame") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "stack-list-arguments"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdStackListArguments::CreateSelf; +} + +//++ +// Details: CMICmdCmdStackListArguments destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdStackListArguments::~CMICmdCmdStackListArguments() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdStackListArguments::ParseArgs() { + m_setCmdArgs.Add( + new CMICmdArgValPrintValues(m_constStrArgPrintValues, true, true)); + m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgFrameLow, false, true)); + m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgFrameHigh, false, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdStackListArguments::Execute() { + CMICMDBASE_GETOPTION(pArgThread, OptionLong, m_constStrArgThread); + CMICMDBASE_GETOPTION(pArgPrintValues, PrintValues, m_constStrArgPrintValues); + CMICMDBASE_GETOPTION(pArgFrameLow, Number, m_constStrArgFrameLow); + CMICMDBASE_GETOPTION(pArgFrameHigh, Number, m_constStrArgFrameHigh); + + // Retrieve the --thread option's thread ID (only 1) + MIuint64 nThreadId = UINT64_MAX; + if (pArgThread->GetFound()) { + if (!pArgThread->GetExpectedOption( + nThreadId)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND), + m_cmdData.strMiCmd.c_str(), + m_constStrArgThread.c_str())); + return MIstatus::failure; + } + } + + const CMICmnLLDBDebugSessionInfo::VariableInfoFormat_e eVarInfoFormat = + static_cast( + pArgPrintValues->GetValue()); + + MIuint nFrameLow = 0; + MIuint nFrameHigh = UINT32_MAX; + if (pArgFrameLow->GetFound() && pArgFrameHigh->GetFound()) { + nFrameLow = pArgFrameLow->GetValue(); + nFrameHigh = pArgFrameHigh->GetValue() + 1; + } else if (pArgFrameLow->GetFound() || pArgFrameHigh->GetFound()) { + // Only low-frame or high-frame was specified but both are required + SetError( + CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_THREAD_FRAME_RANGE_INVALID), + m_cmdData.strMiCmd.c_str())); + return MIstatus::failure; + } + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); + lldb::SBThread thread = (nThreadId != UINT64_MAX) + ? sbProcess.GetThreadByIndexID(nThreadId) + : sbProcess.GetSelectedThread(); + m_bThreadInvalid = !thread.IsValid(); + if (m_bThreadInvalid) + return MIstatus::success; + + const lldb::StopReason eStopReason = thread.GetStopReason(); + if ((eStopReason == lldb::eStopReasonNone) || + (eStopReason == lldb::eStopReasonInvalid)) { + m_bThreadInvalid = true; + return MIstatus::success; + } + + const MIuint nFrames = thread.GetNumFrames(); + if (nFrameLow >= nFrames) { + // The low-frame is larger than the actual number of frames + SetError( + CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_THREAD_FRAME_RANGE_INVALID), + m_cmdData.strMiCmd.c_str())); + return MIstatus::failure; + } + + nFrameHigh = std::min(nFrameHigh, nFrames); + for (MIuint i = nFrameLow; i < nFrameHigh; i++) { + lldb::SBFrame frame = thread.GetFrameAtIndex(i); + CMICmnMIValueList miValueList(true); + const MIuint maskVarTypes = + CMICmnLLDBDebugSessionInfo::eVariableType_Arguments; + if (!rSessionInfo.MIResponseFormVariableInfo(frame, maskVarTypes, + eVarInfoFormat, miValueList)) + return MIstatus::failure; + const CMICmnMIValueConst miValueConst(CMIUtilString::Format("%d", i)); + const CMICmnMIValueResult miValueResult("level", miValueConst); + CMICmnMIValueTuple miValueTuple(miValueResult); + const CMICmnMIValueResult miValueResult2("args", miValueList); + miValueTuple.Add(miValueResult2); + const CMICmnMIValueResult miValueResult3("frame", miValueTuple); + m_miValueList.Add(miValueResult3); + } + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdStackListArguments::Acknowledge() { + if (m_bThreadInvalid) { + // MI print "%s^done,stack-args=[]" + const CMICmnMIValueList miValueList(true); + const CMICmnMIValueResult miValueResult("stack-args", miValueList); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + + // MI print + // "%s^done,stack-args=[frame={level=\"0\",args=[%s]},frame={level=\"1\",args=[%s]}]" + const CMICmnMIValueResult miValueResult4("stack-args", m_miValueList); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult4); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdStackListArguments::CreateSelf() { + return new CMICmdCmdStackListArguments(); +} + + +//++ +// Details: CMICmdCmdStackListLocals constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdStackListLocals::CMICmdCmdStackListLocals() + : m_bThreadInvalid(false), m_miValueList(true), + m_constStrArgPrintValues("print-values") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "stack-list-locals"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdStackListLocals::CreateSelf; +} + +//++ +// Details: CMICmdCmdStackListLocals destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdStackListLocals::~CMICmdCmdStackListLocals() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdStackListLocals::ParseArgs() { + m_setCmdArgs.Add( + new CMICmdArgValPrintValues(m_constStrArgPrintValues, true, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdStackListLocals::Execute() { + CMICMDBASE_GETOPTION(pArgThread, OptionLong, m_constStrArgThread); + CMICMDBASE_GETOPTION(pArgFrame, OptionLong, m_constStrArgFrame); + CMICMDBASE_GETOPTION(pArgPrintValues, PrintValues, m_constStrArgPrintValues); + + // Retrieve the --thread option's thread ID (only 1) + MIuint64 nThreadId = UINT64_MAX; + if (pArgThread->GetFound()) { + if (!pArgThread->GetExpectedOption( + nThreadId)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND), + m_cmdData.strMiCmd.c_str(), + m_constStrArgThread.c_str())); + return MIstatus::failure; + } + } + + MIuint64 nFrame = UINT64_MAX; + if (pArgFrame->GetFound()) { + if (!pArgFrame->GetExpectedOption(nFrame)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND), + m_cmdData.strMiCmd.c_str(), + m_constStrArgFrame.c_str())); + return MIstatus::failure; + } + } + + const CMICmnLLDBDebugSessionInfo::VariableInfoFormat_e eVarInfoFormat = + static_cast( + pArgPrintValues->GetValue()); + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); + lldb::SBThread thread = (nThreadId != UINT64_MAX) + ? sbProcess.GetThreadByIndexID(nThreadId) + : sbProcess.GetSelectedThread(); + m_bThreadInvalid = !thread.IsValid(); + if (m_bThreadInvalid) + return MIstatus::success; + + const lldb::StopReason eStopReason = thread.GetStopReason(); + if ((eStopReason == lldb::eStopReasonNone) || + (eStopReason == lldb::eStopReasonInvalid)) { + m_bThreadInvalid = true; + return MIstatus::success; + } + + lldb::SBFrame frame = (nFrame != UINT64_MAX) ? thread.GetFrameAtIndex(nFrame) + : thread.GetSelectedFrame(); + + CMICmnMIValueList miValueList(true); + const MIuint maskVarTypes = CMICmnLLDBDebugSessionInfo::eVariableType_Locals | + CMICmnLLDBDebugSessionInfo::eVariableType_InScope; + if (!rSessionInfo.MIResponseFormVariableInfo(frame, maskVarTypes, + eVarInfoFormat, miValueList)) + return MIstatus::failure; + + m_miValueList = miValueList; + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdStackListLocals::Acknowledge() { + if (m_bThreadInvalid) { + // MI print "%s^done,locals=[]" + const CMICmnMIValueList miValueList(true); + const CMICmnMIValueResult miValueResult("locals", miValueList); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + + // MI print "%s^done,locals=[%s]" + const CMICmnMIValueResult miValueResult("locals", m_miValueList); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdStackListLocals::CreateSelf() { + return new CMICmdCmdStackListLocals(); +} + + +//++ +// Details: CMICmdCmdStackListVariables constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdStackListVariables::CMICmdCmdStackListVariables() + : m_bThreadInvalid(false), m_miValueList(true), + m_constStrArgPrintValues("print-values") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "stack-list-variables"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdStackListVariables::CreateSelf; +} + +//++ +// Details: CMICmdCmdStackListVariables destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdStackListVariables::~CMICmdCmdStackListVariables() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdStackListVariables::ParseArgs() { + m_setCmdArgs.Add( + new CMICmdArgValPrintValues(m_constStrArgPrintValues, true, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdStackListVariables::Execute() { + CMICMDBASE_GETOPTION(pArgThread, OptionLong, m_constStrArgThread); + CMICMDBASE_GETOPTION(pArgFrame, OptionLong, m_constStrArgFrame); + CMICMDBASE_GETOPTION(pArgPrintValues, PrintValues, m_constStrArgPrintValues); + + // Retrieve the --thread option's thread ID (only 1) + MIuint64 nThreadId = UINT64_MAX; + if (pArgThread->GetFound()) { + if (!pArgThread->GetExpectedOption( + nThreadId)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND), + m_cmdData.strMiCmd.c_str(), + m_constStrArgThread.c_str())); + return MIstatus::failure; + } + } + + MIuint64 nFrame = UINT64_MAX; + if (pArgFrame->GetFound()) { + if (!pArgFrame->GetExpectedOption(nFrame)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND), + m_cmdData.strMiCmd.c_str(), + m_constStrArgFrame.c_str())); + return MIstatus::failure; + } + } + + const CMICmnLLDBDebugSessionInfo::VariableInfoFormat_e eVarInfoFormat = + static_cast( + pArgPrintValues->GetValue()); + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); + lldb::SBThread thread = (nThreadId != UINT64_MAX) + ? sbProcess.GetThreadByIndexID(nThreadId) + : sbProcess.GetSelectedThread(); + m_bThreadInvalid = !thread.IsValid(); + if (m_bThreadInvalid) + return MIstatus::success; + + const lldb::StopReason eStopReason = thread.GetStopReason(); + if ((eStopReason == lldb::eStopReasonNone) || + (eStopReason == lldb::eStopReasonInvalid)) { + m_bThreadInvalid = true; + return MIstatus::success; + } + + lldb::SBFrame frame = (nFrame != UINT64_MAX) ? thread.GetFrameAtIndex(nFrame) + : thread.GetSelectedFrame(); + + CMICmnMIValueList miValueList(true); + const MIuint maskVarTypes = + CMICmnLLDBDebugSessionInfo::eVariableType_Arguments | + CMICmnLLDBDebugSessionInfo::eVariableType_Locals | + CMICmnLLDBDebugSessionInfo::eVariableType_InScope; + if (!rSessionInfo.MIResponseFormVariableInfo( + frame, maskVarTypes, eVarInfoFormat, miValueList, 10, true)) + return MIstatus::failure; + m_miValueList = miValueList; + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdStackListVariables::Acknowledge() { + if (m_bThreadInvalid) { + // MI print "%s^done,variables=[]" + const CMICmnMIValueList miValueList(true); + const CMICmnMIValueResult miValueResult("variables", miValueList); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + + // MI print "%s^done,variables=[%s]" + const CMICmnMIValueResult miValueResult("variables", m_miValueList); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdStackListVariables::CreateSelf() { + return new CMICmdCmdStackListVariables(); +} + + +//++ +// Details: CMICmdCmdStackSelectFrame constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdStackSelectFrame::CMICmdCmdStackSelectFrame() + : m_bFrameInvalid(false), m_constStrArgFrameId("frame_id") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "stack-select-frame"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdStackSelectFrame::CreateSelf; +} + +//++ +// Details: CMICmdCmdStackSelectFrame destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdStackSelectFrame::~CMICmdCmdStackSelectFrame() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdStackSelectFrame::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgFrameId, true, false)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdStackSelectFrame::Execute() { + CMICMDBASE_GETOPTION(pArgFrame, Number, m_constStrArgFrameId); + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBThread sbThread = rSessionInfo.GetProcess().GetSelectedThread(); + + const MIuint nFrameId = pArgFrame->GetValue(); + m_bFrameInvalid = (nFrameId >= sbThread.GetNumFrames()); + if (m_bFrameInvalid) + return MIstatus::success; + + lldb::SBFrame sbFrame = sbThread.SetSelectedFrame(nFrameId); + m_bFrameInvalid = !sbFrame.IsValid(); + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmdCmdStackSelectFrame::Acknowledge() { + if (m_bFrameInvalid) { + // MI print "%s^error,msg=\"Command '-stack-select-frame'. Frame ID + // invalid\"" + const CMICmnMIValueConst miValueConst(CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_FRAME_INVALID), m_cmdData.strMiCmd.c_str())); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; + } + + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdStackSelectFrame::CreateSelf() { + return new CMICmdCmdStackSelectFrame(); +} diff --git a/src/MICmdCmdStack.h b/src/MICmdCmdStack.h new file mode 100644 index 00000000000..04aeb5db034 --- /dev/null +++ b/src/MICmdCmdStack.h @@ -0,0 +1,256 @@ +//===-- MICmdCmdStack.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdStackInfoDepth interface. +// CMICmdCmdStackInfoFrame interface. +// CMICmdCmdStackListFrames interface. +// CMICmdCmdStackListArguments interface. +// CMICmdCmdStackListLocals interface. +// CMICmdCmdStackSelectFrame interface. +// +// To implement new MI commands derive a new command class from the +// command base +// class. To enable the new command for interpretation add the new +// command class +// to the command factory. The files of relevance are: +// MICmdCommands.cpp +// MICmdBase.h / .cpp +// MICmdCmd.h / .cpp +// For an introduction to adding a new command see +// CMICmdCmdSupportInfoMiCmdQuery +// command class as an example. + +#pragma once + +// In-house headers: +#include "MICmdBase.h" +#include "MICmnMIValueList.h" +#include "MICmnMIValueTuple.h" + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "stack-info-depth". +//-- +class CMICmdCmdStackInfoDepth : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdStackInfoDepth(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdStackInfoDepth() override; + + // Attributes: +private: + MIuint m_nThreadFrames; + const CMIUtilString m_constStrArgMaxDepth; // Not handled by *this command +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "stack-info-frame". +//-- +class CMICmdCmdStackInfoFrame : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdStackInfoFrame(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdStackInfoFrame() override; + + // Attributes: +private: + CMICmnMIValueTuple m_miValueTuple; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "stack-list-frames". +//-- +class CMICmdCmdStackListFrames : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdStackListFrames(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdStackListFrames() override; + + // Typedefs: +private: + typedef std::vector VecMIValueResult_t; + + // Attributes: +private: + MIuint m_nThreadFrames; + VecMIValueResult_t m_vecMIValueResult; + const CMIUtilString m_constStrArgFrameLow; + const CMIUtilString m_constStrArgFrameHigh; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "stack-list-arguments". +//-- +class CMICmdCmdStackListArguments : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdStackListArguments(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdStackListArguments() override; + + // Attributes: +private: + bool m_bThreadInvalid; // True = yes invalid thread, false = thread object + // valid + CMICmnMIValueList m_miValueList; + const CMIUtilString m_constStrArgPrintValues; + const CMIUtilString m_constStrArgFrameLow; + const CMIUtilString m_constStrArgFrameHigh; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "stack-list-locals". +//-- +class CMICmdCmdStackListLocals : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdStackListLocals(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdStackListLocals() override; + + // Attributes: +private: + bool m_bThreadInvalid; // True = yes invalid thread, false = thread object + // valid + CMICmnMIValueList m_miValueList; + const CMIUtilString m_constStrArgPrintValues; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "stack-list-variables". +//-- +class CMICmdCmdStackListVariables : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdStackListVariables(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdStackListVariables() override; + + // Attributes +private: + bool m_bThreadInvalid; // True = yes invalid thread, false = thread object + // valid + CMICmnMIValueList m_miValueList; + const CMIUtilString m_constStrArgPrintValues; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "stack-select-frame". +//-- +class CMICmdCmdStackSelectFrame : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdStackSelectFrame(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdStackSelectFrame() override; + + // Attributes: +private: + bool m_bFrameInvalid; // True = yes invalid frame, false = ok + const CMIUtilString m_constStrArgFrameId; +}; diff --git a/src/MICmdCmdSupportInfo.cpp b/src/MICmdCmdSupportInfo.cpp new file mode 100644 index 00000000000..e17f70a9047 --- /dev/null +++ b/src/MICmdCmdSupportInfo.cpp @@ -0,0 +1,117 @@ +//===-- MICmdCmdSupportInfo.cpp ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdSupportInfoMiCmdQuery implementation. + +// In-house headers: +#include "MICmdCmdSupportInfo.h" +#include "MICmdArgValString.h" +#include "MICmdFactory.h" +#include "MICmnMIResultRecord.h" +#include "MICmnMIValueConst.h" +#include "MICmnMIValueTuple.h" + +//++ +// Details: CMICmdCmdSupportInfoMiCmdQuery constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdSupportInfoMiCmdQuery::CMICmdCmdSupportInfoMiCmdQuery() + : m_bCmdFound(false), m_constStrArgCmdName("cmd_name") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "info-gdb-mi-command"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdSupportInfoMiCmdQuery::CreateSelf; +} + +//++ +// Details: CMICmdCmdSupportInfoMiCmdQuery destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdSupportInfoMiCmdQuery::~CMICmdCmdSupportInfoMiCmdQuery() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdSupportInfoMiCmdQuery::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgCmdName, true, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdSupportInfoMiCmdQuery::Execute() { + CMICMDBASE_GETOPTION(pArgNamedCmdName, String, m_constStrArgCmdName); + const CMIUtilString &rCmdToQuery(pArgNamedCmdName->GetValue()); + const MIuint nLen = rCmdToQuery.length(); + const CMICmdFactory &rCmdFactory = CMICmdFactory::Instance(); + if ((nLen > 1) && (rCmdToQuery[0] == '-')) + m_bCmdFound = rCmdFactory.CmdExist(rCmdToQuery.substr(1, nLen - 1).c_str()); + else + m_bCmdFound = rCmdFactory.CmdExist(rCmdToQuery); + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdSupportInfoMiCmdQuery::Acknowledge() { + const CMICmnMIValueConst miValueConst(m_bCmdFound ? "true" : "false"); + const CMICmnMIValueResult miValueResult("exists", miValueConst); + const CMICmnMIValueTuple miValueTuple(miValueResult); + const CMICmnMIValueResult miValueResult2("command", miValueTuple); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult2); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdSupportInfoMiCmdQuery::CreateSelf() { + return new CMICmdCmdSupportInfoMiCmdQuery(); +} diff --git a/src/MICmdCmdSupportInfo.h b/src/MICmdCmdSupportInfo.h new file mode 100644 index 00000000000..ba3e4a6c66a --- /dev/null +++ b/src/MICmdCmdSupportInfo.h @@ -0,0 +1,58 @@ +//===-- MICmdCmdSupportInfo.h -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdSupportInfoMiCmdQuery interface. +// +// To implement new MI commands derive a new command class from the +// command base +// class. To enable the new command for interpretation add the new +// command class +// to the command factory. The files of relevance are: +// MICmdCommands.cpp +// MICmdBase.h / .cpp +// MICmdCmd.h / .cpp +// For an introduction to adding a new command see +// CMICmdCmdSupportInfoMiCmdQuery +// command class as an example. + +#pragma once + +// In-house headers: +#include "MICmdBase.h" + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "info-gdb-mi-command". +// This command does not follow the MI documentation exactly. +//-- +class CMICmdCmdSupportInfoMiCmdQuery : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdSupportInfoMiCmdQuery(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdSupportInfoMiCmdQuery() override; + + // Attributes: +private: + bool m_bCmdFound; // True = query for the command in command factory found, + // false = not found not recognised + const CMIUtilString m_constStrArgCmdName; +}; diff --git a/src/MICmdCmdSupportList.cpp b/src/MICmdCmdSupportList.cpp new file mode 100644 index 00000000000..e457e0101f4 --- /dev/null +++ b/src/MICmdCmdSupportList.cpp @@ -0,0 +1,96 @@ +//===-- MICmdCmdSupportList.cpp ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdSupportListFeatures implementation. + +// In-house headers: +#include "MICmdCmdSupportList.h" +#include "MICmnMIResultRecord.h" +#include "MICmnMIValueConst.h" +#include "MICmnMIValueList.h" + +//++ +// Details: CMICmdCmdSupportListFeatures constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdSupportListFeatures::CMICmdCmdSupportListFeatures() { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "list-features"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdSupportListFeatures::CreateSelf; +} + +//++ +// Details: CMICmdCmdSupportListFeatures destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdSupportListFeatures::~CMICmdCmdSupportListFeatures() {} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdSupportListFeatures::Execute() { + // Do nothing + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdSupportListFeatures::Acknowledge() { + // Declare supported features here + const CMICmnMIValueConst miValueConst1("data-read-memory-bytes"); + const CMICmnMIValueConst miValueConst2("exec-run-start-option"); + // Some features may depend on host and/or target, decide what to add below + CMICmnMIValueList miValueList(true); + miValueList.Add(miValueConst1); + miValueList.Add(miValueConst2); + const CMICmnMIValueResult miValueResult("features", miValueList); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdSupportListFeatures::CreateSelf() { + return new CMICmdCmdSupportListFeatures(); +} diff --git a/src/MICmdCmdSupportList.h b/src/MICmdCmdSupportList.h new file mode 100644 index 00000000000..9e8a64bc11a --- /dev/null +++ b/src/MICmdCmdSupportList.h @@ -0,0 +1,51 @@ +//===-- MICmdCmdSupportList.h -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdSupportListFeatures interface. +// +// To implement new MI commands derive a new command class from the +// command base +// class. To enable the new command for interpretation add the new +// command class +// to the command factory. The files of relevance are: +// MICmdCommands.cpp +// MICmdBase.h / .cpp +// MICmdCmd.h / .cpp +// For an introduction to adding a new command see +// CMICmdCmdSupportInfoMiCmdQuery +// command class as an example. + +#pragma once + +// In-house headers: +#include "MICmdBase.h" + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "list-features". +// This command does not follow the MI documentation exactly. +//-- +class CMICmdCmdSupportListFeatures : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdSupportListFeatures(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdSupportListFeatures() override; +}; diff --git a/src/MICmdCmdSymbol.cpp b/src/MICmdCmdSymbol.cpp new file mode 100644 index 00000000000..d57d1b008e5 --- /dev/null +++ b/src/MICmdCmdSymbol.cpp @@ -0,0 +1,177 @@ +//===-- MICmdCmdSymbol.cpp --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdSymbolListLines implementation. + +// Third Party Headers: +#include "llvm/ADT/Twine.h" +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBLineEntry.h" +#include "lldb/API/SBFileSpec.h" +#include "lldb/API/SBCompileUnit.h" +#include "lldb/API/SBSymbolContext.h" +#include "lldb/API/SBSymbolContextList.h" + +// In-house headers: +#include "MICmdArgValFile.h" +#include "MICmdCmdSymbol.h" +#include "MICmnLLDBDebugSessionInfo.h" +#include "MICmnMIResultRecord.h" +#include "MICmnMIValueTuple.h" +#include "MICmnMIValueResult.h" + +namespace { +const CMICmnMIValueTuple +CreateMITuplePCLine(const uint32_t addr, const uint32_t line_number) { + const CMICmnMIValueConst miValueConstAddr("0x" + llvm::Twine::utohexstr(addr).str()); + const CMICmnMIValueConst miValueConstLine(llvm::Twine(line_number).str()); + const CMICmnMIValueResult miValueResultAddr("pc", miValueConstAddr); + const CMICmnMIValueResult miValueResultLine("line", miValueConstLine); + CMICmnMIValueTuple miValueTuple(miValueResultAddr); + miValueTuple.Add(miValueResultLine); + return miValueTuple; +} +} // namespace + +using namespace lldb; // For operator==(const SBAddress &, const SBAddress &). + +//++ +// Details: CMICmdCmdSymbolListLines constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdSymbolListLines::CMICmdCmdSymbolListLines() + : m_resultList(false), m_constStrArgNameFile("file") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "symbol-list-lines"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdSymbolListLines::CreateSelf; +} + +//++ +// Details: CMICmdCmdSymbolListLines destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdSymbolListLines::~CMICmdCmdSymbolListLines() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdSymbolListLines::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValFile(m_constStrArgNameFile, true, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Synopsis: -symbol-list-lines file +// Ref: +// http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Symbol-Query.html#GDB_002fMI-Symbol-Query +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdSymbolListLines::Execute() { + CMICMDBASE_GETOPTION(pArgFile, File, m_constStrArgNameFile); + + const auto &rSessionInfo(CMICmnLLDBDebugSessionInfo::Instance()); + if (rSessionInfo.GetTarget() == rSessionInfo.GetDebugger().GetDummyTarget()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_TARGET_CURRENT), + m_cmdData.strMiCmd.c_str())); + return MIstatus::failure; + } + + const lldb::SBFileSpec source_file_spec(pArgFile->GetValue().c_str(), true); + const char *source_file_name = source_file_spec.GetFilename(); + const char *source_file_directory = source_file_spec.GetDirectory(); + const bool has_path = source_file_directory; + + lldb::SBSymbolContextList sc_cu_list = + CMICmnLLDBDebugSessionInfo::Instance().GetTarget().FindCompileUnits( + source_file_spec); + + bool found_something = false; + for (uint32_t i = 0, e = sc_cu_list.GetSize(); i < e; ++i) { + const lldb::SBCompileUnit cu = + sc_cu_list.GetContextAtIndex(i).GetCompileUnit(); + for (uint32_t j = 0, e = cu.GetNumLineEntries(); j < e; ++j) { + const lldb::SBLineEntry line = cu.GetLineEntryAtIndex(j); + const lldb::SBFileSpec line_spec = line.GetFileSpec(); + if (line_spec.GetFilename() == source_file_name) { + if (has_path && (line_spec.GetDirectory() != source_file_directory)) + continue; + // We don't need a line with start address equals to end one, + // so just skip it. + const lldb::SBAddress line_start_address = line.GetStartAddress(); + const lldb::SBAddress line_end_address = line.GetEndAddress(); + if (line_start_address == line_end_address) + continue; + // We have a matching line. + found_something = true; + m_resultList.Add(CreateMITuplePCLine( + line_start_address.GetFileAddress(), + line.GetLine())); + } + } + } + if (!found_something) { + SetError(MIRSRC(IDS_UTIL_FILE_ERR_INVALID_PATHNAME)); + return MIstatus::failure; + } + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdSymbolListLines::Acknowledge() { + // MI print "%s^done,lines=[{pc=\"%d\",line=\"%d\"}...]" + const CMICmnMIValueResult miValueResult("lines", m_resultList); + m_miResultRecord = CMICmnMIResultRecord( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdSymbolListLines::CreateSelf() { + return new CMICmdCmdSymbolListLines(); +} diff --git a/src/MICmdCmdSymbol.h b/src/MICmdCmdSymbol.h new file mode 100644 index 00000000000..839b0153a77 --- /dev/null +++ b/src/MICmdCmdSymbol.h @@ -0,0 +1,59 @@ +//===-- MICmdCmdSymbol.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdSymbolListLines interface. +// +// To implement new MI commands derive a new command class from the +// command base +// class. To enable the new command for interpretation add the new +// command class +// to the command factory. The files of relevance are: +// MICmdCommands.cpp +// MICmdBase.h / .cpp +// MICmdCmd.h / .cpp +// For an introduction to adding a new command see +// CMICmdCmdSupportInfoMiCmdQuery +// command class as an example. + +#pragma once + +// Third party headers: + +// In-house headers: +#include "MICmdBase.h" +#include "MICmnMIValueList.h" + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "symbol-list-lines". +//-- +class CMICmdCmdSymbolListLines : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdSymbolListLines(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdSymbolListLines() override; + + // Attributes: +private: + CMICmnMIValueList m_resultList; + const CMIUtilString m_constStrArgNameFile; +}; diff --git a/src/MICmdCmdTarget.cpp b/src/MICmdCmdTarget.cpp new file mode 100644 index 00000000000..18ce038b168 --- /dev/null +++ b/src/MICmdCmdTarget.cpp @@ -0,0 +1,447 @@ +//===-- MICmdCmdTarget.cpp --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdTargetSelect implementation. + +// Third Party Headers: +#include "lldb/API/SBStream.h" +#include "lldb/API/SBError.h" + +// In-house headers: +#include "MICmdArgValNumber.h" +#include "MICmdArgValOptionLong.h" +#include "MICmdArgValOptionShort.h" +#include "MICmdArgValString.h" +#include "MICmdCmdTarget.h" +#include "MICmnLLDBDebugSessionInfo.h" +#include "MICmnLLDBDebugger.h" +#include "MICmnMIOutOfBandRecord.h" +#include "MICmnMIResultRecord.h" +#include "MICmnMIValueConst.h" + +//++ +// Details: CMICmdCmdTargetSelect constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdTargetSelect::CMICmdCmdTargetSelect() + : m_constStrArgNamedType("type"), + m_constStrArgNamedParameters("parameters") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "target-select"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdTargetSelect::CreateSelf; +} + +//++ +// Details: CMICmdCmdTargetSelect destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdTargetSelect::~CMICmdCmdTargetSelect() = default; + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdTargetSelect::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgNamedType, true, true)); + m_setCmdArgs.Add( + new CMICmdArgValString(m_constStrArgNamedParameters, true, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Synopsis: -target-select type parameters ... +// Ref: +// http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Target-Manipulation.html#GDB_002fMI-Target-Manipulation +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdTargetSelect::Execute() { + CMICMDBASE_GETOPTION(pArgType, String, m_constStrArgNamedType); + CMICMDBASE_GETOPTION(pArgParameters, String, m_constStrArgNamedParameters); + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBTarget target = rSessionInfo.GetTarget(); + + // Check we have a valid target. + // Note: target created via 'file-exec-and-symbols' command. + if (!target.IsValid()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_TARGET_CURRENT), + m_cmdData.strMiCmd.c_str())); + return MIstatus::failure; + } + + // Verify that we are executing remotely. + const CMIUtilString &rRemoteType(pArgType->GetValue()); + if (rRemoteType != "remote") { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_TARGET_TYPE), + m_cmdData.strMiCmd.c_str(), + rRemoteType.c_str())); + return MIstatus::failure; + } + + // Create a URL pointing to the remote gdb stub. + const CMIUtilString strUrl = + CMIUtilString::Format("connect://%s", pArgParameters->GetValue().c_str()); + + lldb::SBError error; + // Ask LLDB to connect to the target port. + const char *pPlugin("gdb-remote"); + lldb::SBProcess process = target.ConnectRemote( + rSessionInfo.GetListener(), strUrl.c_str(), pPlugin, error); + + // Verify that we have managed to connect successfully. + if (!process.IsValid()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_TARGET_PLUGIN), + m_cmdData.strMiCmd.c_str(), + error.GetCString())); + return MIstatus::failure; + } + + // Set the environment path if we were given one. + CMIUtilString strWkDir; + if (rSessionInfo.SharedDataRetrieve( + rSessionInfo.m_constStrSharedDataKeyWkDir, strWkDir)) { + lldb::SBDebugger &rDbgr = rSessionInfo.GetDebugger(); + if (!rDbgr.SetCurrentPlatformSDKRoot(strWkDir.c_str())) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_FNFAILED), + m_cmdData.strMiCmd.c_str(), + "target-select")); + return MIstatus::failure; + } + } + + // Set the shared object path if we were given one. + CMIUtilString strSolibPath; + if (rSessionInfo.SharedDataRetrieve( + rSessionInfo.m_constStrSharedDataSolibPath, strSolibPath)) + target.AppendImageSearchPath(".", strSolibPath.c_str(), error); + + return HandleSBError(error); +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdTargetSelect::Acknowledge() { + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Connected); + m_miResultRecord = miRecordResult; + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::pid_t pid = rSessionInfo.GetProcess().GetProcessID(); + // Prod the client i.e. Eclipse with out-of-band results to help it 'continue' + // because it is using LLDB debugger + // Give the client '=thread-group-started,id="i1"' + m_bHasResultRecordExtra = true; + const CMICmnMIValueConst miValueConst2("i1"); + const CMICmnMIValueResult miValueResult2("id", miValueConst2); + const CMIUtilString strPid(CMIUtilString::Format("%lld", pid)); + const CMICmnMIValueConst miValueConst(strPid); + const CMICmnMIValueResult miValueResult("pid", miValueConst); + CMICmnMIOutOfBandRecord miOutOfBand( + CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupStarted, miValueResult2); + miOutOfBand.Add(miValueResult); + m_miResultRecordExtra = miOutOfBand.GetString(); + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdTargetSelect::CreateSelf() { + return new CMICmdCmdTargetSelect(); +} + +//++ +// Details: CMICmdCmdTargetAttach constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdTargetAttach::CMICmdCmdTargetAttach() + : m_constStrArgPid("pid"), m_constStrArgNamedFile("n"), + m_constStrArgWaitFor("waitfor") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "target-attach"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdTargetAttach::CreateSelf; +} + +//++ +// Details: CMICmdCmdTargetAttach destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdTargetAttach::~CMICmdCmdTargetAttach() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdTargetAttach::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgPid, false, true)); + m_setCmdArgs.Add( + new CMICmdArgValOptionShort(m_constStrArgNamedFile, false, true, + CMICmdArgValListBase::eArgValType_String, 1)); + m_setCmdArgs.Add( + new CMICmdArgValOptionLong(m_constStrArgWaitFor, false, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Synopsis: -target-attach file +// Ref: +// http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Target-Manipulation.html#GDB_002fMI-Target-Manipulation +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdTargetAttach::Execute() { + CMICMDBASE_GETOPTION(pArgPid, Number, m_constStrArgPid); + CMICMDBASE_GETOPTION(pArgFile, OptionShort, m_constStrArgNamedFile); + CMICMDBASE_GETOPTION(pArgWaitFor, OptionLong, m_constStrArgWaitFor); + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + + // If the current target is invalid, create one + lldb::SBTarget target = rSessionInfo.GetTarget(); + if (!target.IsValid()) { + target = rSessionInfo.GetDebugger().CreateTarget(nullptr); + if (!target.IsValid()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_TARGET_CURRENT), + m_cmdData.strMiCmd.c_str())); + return MIstatus::failure; + } + } + + lldb::SBError error; + lldb::SBListener listener; + if (pArgPid->GetFound() && pArgPid->GetValid()) { + lldb::pid_t pid; + pid = pArgPid->GetValue(); + target.AttachToProcessWithID(listener, pid, error); + } else if (pArgFile->GetFound() && pArgFile->GetValid()) { + bool bWaitFor = (pArgWaitFor->GetFound()); + CMIUtilString file; + pArgFile->GetExpectedOption(file); + target.AttachToProcessWithName(listener, file.c_str(), bWaitFor, error); + } else { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_ATTACH_BAD_ARGS), + m_cmdData.strMiCmd.c_str())); + return MIstatus::failure; + } + + lldb::SBStream errMsg; + if (error.Fail()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_ATTACH_FAILED), + m_cmdData.strMiCmd.c_str(), + errMsg.GetData())); + return MIstatus::failure; + } + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdTargetAttach::Acknowledge() { + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); + m_miResultRecord = miRecordResult; + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::pid_t pid = rSessionInfo.GetProcess().GetProcessID(); + // Prod the client i.e. Eclipse with out-of-band results to help it 'continue' + // because it is using LLDB debugger + // Give the client '=thread-group-started,id="i1"' + m_bHasResultRecordExtra = true; + const CMICmnMIValueConst miValueConst2("i1"); + const CMICmnMIValueResult miValueResult2("id", miValueConst2); + const CMIUtilString strPid(CMIUtilString::Format("%lld", pid)); + const CMICmnMIValueConst miValueConst(strPid); + const CMICmnMIValueResult miValueResult("pid", miValueConst); + CMICmnMIOutOfBandRecord miOutOfBand( + CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupStarted, miValueResult2); + miOutOfBand.Add(miValueResult); + m_miResultRecordExtra = miOutOfBand.GetString(); + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdTargetAttach::CreateSelf() { + return new CMICmdCmdTargetAttach(); +} + +//++ +// Details: CMICmdCmdTargetDetach constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdTargetDetach::CMICmdCmdTargetDetach() { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "target-detach"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdTargetDetach::CreateSelf; +} + +//++ +// Details: CMICmdCmdTargetDetach destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdTargetDetach::~CMICmdCmdTargetDetach() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdTargetDetach::ParseArgs() { return MIstatus::success; } + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Synopsis: -target-attach file +// Ref: +// http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Target-Manipulation.html#GDB_002fMI-Target-Manipulation +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdTargetDetach::Execute() { + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + + lldb::SBProcess process = rSessionInfo.GetProcess(); + + if (!process.IsValid()) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_PROCESS), + m_cmdData.strMiCmd.c_str())); + return MIstatus::failure; + } + + process.Detach(); + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdTargetDetach::Acknowledge() { + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); + m_miResultRecord = miRecordResult; + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdTargetDetach::CreateSelf() { + return new CMICmdCmdTargetDetach(); +} diff --git a/src/MICmdCmdTarget.h b/src/MICmdCmdTarget.h new file mode 100644 index 00000000000..5cb140fd68b --- /dev/null +++ b/src/MICmdCmdTarget.h @@ -0,0 +1,117 @@ +//===-- MICmdCmdTarget.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdTargetSelect interface. +// +// To implement new MI commands derive a new command class from the +// command base +// class. To enable the new command for interpretation add the new +// command class +// to the command factory. The files of relevance are: +// MICmdCommands.cpp +// MICmdBase.h / .cpp +// MICmdCmd.h / .cpp +// For an introduction to adding a new command see +// CMICmdCmdSupportInfoMiCmdQuery +// command class as an example. + +#pragma once + +// In-house headers: +#include "MICmdBase.h" +#include "MICmnMIValueList.h" +#include "MICmnMIValueTuple.h" + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "target-select". +// http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Target-Manipulation.html#GDB_002fMI-Target-Manipulation +//-- +class CMICmdCmdTargetSelect : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdTargetSelect(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdTargetSelect() override; + + // Attributes: +private: + const CMIUtilString m_constStrArgNamedType; + const CMIUtilString m_constStrArgNamedParameters; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "target-attach". +// http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Target-Manipulation.html#GDB_002fMI-Target-Manipulation +//-- +class CMICmdCmdTargetAttach : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdTargetAttach(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdTargetAttach() override; + + // Attributes: +private: + const CMIUtilString m_constStrArgPid; + const CMIUtilString m_constStrArgNamedFile; + const CMIUtilString m_constStrArgWaitFor; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "target-attach". +// http://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI-Target-Manipulation.html#GDB_002fMI-Target-Manipulation +//-- +class CMICmdCmdTargetDetach : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdTargetDetach(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdTargetDetach() override; +}; diff --git a/src/MICmdCmdThread.cpp b/src/MICmdCmdThread.cpp new file mode 100644 index 00000000000..e0c74f925a9 --- /dev/null +++ b/src/MICmdCmdThread.cpp @@ -0,0 +1,211 @@ +//===-- MICmdCmdThread.cpp --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdThreadInfo implementation. + +// Third Party Headers: +#include "lldb/API/SBBreakpointLocation.h" +#include "lldb/API/SBThread.h" + +// In-house headers: +#include "MICmdArgValNumber.h" +#include "MICmdCmdThread.h" +#include "MICmnLLDBDebugSessionInfo.h" +#include "MICmnLLDBDebugger.h" +#include "MICmnMIResultRecord.h" +#include "MICmnMIValueConst.h" + +//++ +// Details: CMICmdCmdThreadInfo constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdThreadInfo::CMICmdCmdThreadInfo() + : m_bSingleThread(false), m_bThreadInvalid(true), + m_constStrArgNamedThreadId("thread-id"), m_bHasCurrentThread(false) { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "thread-info"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdThreadInfo::CreateSelf; +} + +//++ +// Details: CMICmdCmdThreadInfo destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdThreadInfo::~CMICmdCmdThreadInfo() { m_vecMIValueTuple.clear(); } + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdThreadInfo::ParseArgs() { + m_setCmdArgs.Add( + new CMICmdArgValNumber(m_constStrArgNamedThreadId, false, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdThreadInfo::Execute() { + CMICMDBASE_GETOPTION(pArgThreadId, Number, m_constStrArgNamedThreadId); + MIuint nThreadId = 0; + if (pArgThreadId->GetFound() && pArgThreadId->GetValid()) { + m_bSingleThread = true; + nThreadId = static_cast(pArgThreadId->GetValue()); + } + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); + lldb::SBThread thread = sbProcess.GetSelectedThread(); + + if (m_bSingleThread) { + thread = sbProcess.GetThreadByIndexID(nThreadId); + m_bThreadInvalid = !thread.IsValid(); + if (m_bThreadInvalid) + return MIstatus::success; + + CMICmnMIValueTuple miTuple; + if (!rSessionInfo.MIResponseFormThreadInfo( + m_cmdData, thread, + CMICmnLLDBDebugSessionInfo::eThreadInfoFormat_AllFrames, miTuple)) + return MIstatus::failure; + + m_miValueTupleThread = miTuple; + + return MIstatus::success; + } + + // Multiple threads + m_vecMIValueTuple.clear(); + const MIuint nThreads = sbProcess.GetNumThreads(); + for (MIuint i = 0; i < nThreads; i++) { + lldb::SBThread thread = sbProcess.GetThreadAtIndex(i); + if (thread.IsValid()) { + CMICmnMIValueTuple miTuple; + if (!rSessionInfo.MIResponseFormThreadInfo( + m_cmdData, thread, + CMICmnLLDBDebugSessionInfo::eThreadInfoFormat_AllFrames, miTuple)) + return MIstatus::failure; + + m_vecMIValueTuple.push_back(miTuple); + } + } + + // -thread-info with multiple threads ends with the current thread id if any + if (thread.IsValid()) { + const CMIUtilString strId(CMIUtilString::Format("%d", thread.GetIndexID())); + CMICmnMIValueConst miValueCurrThreadId(strId); + m_miValueCurrThreadId = miValueCurrThreadId; + m_bHasCurrentThread = true; + } + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdThreadInfo::Acknowledge() { + if (m_bSingleThread) { + if (m_bThreadInvalid) { + const CMICmnMIValueConst miValueConst("invalid thread id"); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + + // MI print + // "%s^done,threads=[{id=\"%d\",target-id=\"%s\",frame={},state=\"%s\"}] + const CMICmnMIValueList miValueList(m_miValueTupleThread); + const CMICmnMIValueResult miValueResult("threads", miValueList); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + + // Build up a list of thread information from tuples + VecMIValueTuple_t::const_iterator it = m_vecMIValueTuple.begin(); + if (it == m_vecMIValueTuple.end()) { + const CMICmnMIValueConst miValueConst("[]"); + const CMICmnMIValueResult miValueResult("threads", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + CMICmnMIValueList miValueList(*it); + ++it; + while (it != m_vecMIValueTuple.end()) { + const CMICmnMIValueTuple &rTuple(*it); + miValueList.Add(rTuple); + + // Next + ++it; + } + + CMICmnMIValueResult miValueResult("threads", miValueList); + if (m_bHasCurrentThread) { + CMIUtilString strCurrThreadId = "current-thread-id"; + miValueResult.Add(strCurrThreadId, m_miValueCurrThreadId); + } + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdThreadInfo::CreateSelf() { + return new CMICmdCmdThreadInfo(); +} diff --git a/src/MICmdCmdThread.h b/src/MICmdCmdThread.h new file mode 100644 index 00000000000..413e293447a --- /dev/null +++ b/src/MICmdCmdThread.h @@ -0,0 +1,69 @@ +//===-- MICmdCmdThread.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdThreadInfo interface. +// +// To implement new MI commands derive a new command class from the +// command base +// class. To enable the new command for interpretation add the new +// command class +// to the command factory. The files of relevance are: +// MICmdCommands.cpp +// MICmdBase.h / .cpp +// MICmdCmd.h / .cpp +// For an introduction to adding a new command see +// CMICmdCmdSupportInfoMiCmdQuery +// command class as an example. + +#pragma once + +// In-house headers: +#include "MICmdBase.h" +#include "MICmnMIValueList.h" +#include "MICmnMIValueTuple.h" + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "thread-info". +//-- +class CMICmdCmdThreadInfo : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdThreadInfo(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdThreadInfo() override; + + // Typedefs: +private: + typedef std::vector VecMIValueTuple_t; + + // Attributes: +private: + CMICmnMIValueTuple m_miValueTupleThread; + bool m_bSingleThread; // True = yes single thread, false = multiple threads + bool m_bThreadInvalid; // True = invalid, false = ok + VecMIValueTuple_t m_vecMIValueTuple; + const CMIUtilString m_constStrArgNamedThreadId; + + // mi value of current-thread-id if multiple threads are requested + bool m_bHasCurrentThread; + CMICmnMIValue m_miValueCurrThreadId; +}; diff --git a/src/MICmdCmdTrace.cpp b/src/MICmdCmdTrace.cpp new file mode 100644 index 00000000000..1daa18010c9 --- /dev/null +++ b/src/MICmdCmdTrace.cpp @@ -0,0 +1,88 @@ +//===-- MICmdCmdTrace.cpp ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdTraceStatus implementation. + +// In-house headers: +#include "MICmdCmdTrace.h" +#include "MICmnMIResultRecord.h" +#include "MICmnMIValueConst.h" + +//++ +// Details: CMICmdCmdTraceStatus constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdTraceStatus::CMICmdCmdTraceStatus() { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "trace-status"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdTraceStatus::CreateSelf; +} + +//++ +// Details: CMICmdCmdTraceStatus destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdTraceStatus::~CMICmdCmdTraceStatus() {} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdTraceStatus::Execute() { + // Do nothing + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdTraceStatus::Acknowledge() { + const CMICmnMIValueConst miValueConst(MIRSRC(IDS_CMD_ERR_NOT_IMPLEMENTED)); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdTraceStatus::CreateSelf() { + return new CMICmdCmdTraceStatus(); +} diff --git a/src/MICmdCmdTrace.h b/src/MICmdCmdTrace.h new file mode 100644 index 00000000000..8796de55b95 --- /dev/null +++ b/src/MICmdCmdTrace.h @@ -0,0 +1,50 @@ +//===-- MICmdCmdTrace.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdTraceStatus interface. +// +// To implement new MI commands derive a new command class from the +// command base +// class. To enable the new command for interpretation add the new +// command class +// to the command factory. The files of relevance are: +// MICmdCommands.cpp +// MICmdBase.h / .cpp +// MICmdCmd.h / .cpp +// For an introduction to adding a new command see +// CMICmdCmdSupportInfoMiCmdQuery +// command class as an example. + +#pragma once + +// In-house headers: +#include "MICmdBase.h" + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "trace-status". +//-- +class CMICmdCmdTraceStatus : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdTraceStatus(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdTraceStatus() override; +}; diff --git a/src/MICmdCmdVar.cpp b/src/MICmdCmdVar.cpp new file mode 100644 index 00000000000..3063b1b464d --- /dev/null +++ b/src/MICmdCmdVar.cpp @@ -0,0 +1,1460 @@ +//===-- MICmdCmdVar.cpp -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdVarCreate implementation. +// CMICmdCmdVarUpdate implementation. +// CMICmdCmdVarDelete implementation. +// CMICmdCmdVarAssign implementation. +// CMICmdCmdVarSetFormat implementation. +// CMICmdCmdVarListChildren implementation. +// CMICmdCmdVarEvaluateExpression implementation. +// CMICmdCmdVarInfoPathExpression implementation. +// CMICmdCmdVarShowAttributes implementation. + +// Third Party Headers: +#include "lldb/API/SBStream.h" +#include "lldb/API/SBThread.h" +#include "lldb/API/SBType.h" + +// In-house headers: +#include "MICmdArgValListOfN.h" +#include "MICmdArgValNumber.h" +#include "MICmdArgValOptionLong.h" +#include "MICmdArgValOptionShort.h" +#include "MICmdArgValPrintValues.h" +#include "MICmdArgValString.h" +#include "MICmdArgValThreadGrp.h" +#include "MICmdCmdVar.h" +#include "MICmnLLDBDebugSessionInfo.h" +#include "MICmnLLDBDebugger.h" +#include "MICmnLLDBProxySBValue.h" +#include "MICmnLLDBUtilSBValue.h" +#include "MICmnMIResultRecord.h" +#include "MICmnMIValueConst.h" + +#include + +//++ +// Details: CMICmdCmdVarCreate constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdVarCreate::CMICmdCmdVarCreate() + : m_nChildren(0), m_nThreadId(0), m_strType("??"), m_bValid(false), + m_strValue("??"), m_constStrArgName("name"), + m_constStrArgFrameAddr("frame-addr"), + m_constStrArgExpression("expression") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "var-create"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdVarCreate::CreateSelf; +} + +//++ +// Details: CMICmdCmdVarCreate destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdVarCreate::~CMICmdCmdVarCreate() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarCreate::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgName, false, true)); + m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgFrameAddr, false, true)); + m_setCmdArgs.Add( + new CMICmdArgValString(m_constStrArgExpression, true, true, true, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarCreate::Execute() { + CMICMDBASE_GETOPTION(pArgThread, OptionLong, m_constStrArgThread); + CMICMDBASE_GETOPTION(pArgFrame, OptionLong, m_constStrArgFrame); + CMICMDBASE_GETOPTION(pArgName, String, m_constStrArgName); + CMICMDBASE_GETOPTION(pArgFrameAddr, String, m_constStrArgFrameAddr); + CMICMDBASE_GETOPTION(pArgExpression, String, m_constStrArgExpression); + + // Retrieve the --thread option's thread ID (only 1) + MIuint64 nThreadId = UINT64_MAX; + if (pArgThread->GetFound() && + !pArgThread->GetExpectedOption(nThreadId)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND), + m_cmdData.strMiCmd.c_str(), + m_constStrArgThread.c_str())); + return MIstatus::failure; + } + + // Retrieve the --frame option's number + MIuint64 nFrame = UINT64_MAX; + if (pArgThread->GetFound() && + !pArgFrame->GetExpectedOption(nFrame)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND), + m_cmdData.strMiCmd.c_str(), + m_constStrArgFrame.c_str())); + return MIstatus::failure; + } + + const CMICmdArgValOptionLong::VecArgObjPtr_t &rVecFrameId( + pArgFrame->GetExpectedOptions()); + CMICmdArgValOptionLong::VecArgObjPtr_t::const_iterator it2 = + rVecFrameId.begin(); + if (it2 != rVecFrameId.end()) { + const CMICmdArgValNumber *pOption = static_cast(*it2); + nFrame = pOption->GetValue(); + } + + m_strVarName = ""; + if (pArgName->GetFound()) { + const CMIUtilString &rArg = pArgName->GetValue(); + const bool bAutoName = (rArg == "-"); + if (bAutoName) { + m_strVarName = CMIUtilString::Format( + "var%u", CMICmnLLDBDebugSessionInfoVarObj::VarObjIdGet()); + CMICmnLLDBDebugSessionInfoVarObj::VarObjIdInc(); + } else + m_strVarName = rArg; + } + + bool bCurrentFrame = false; + if (pArgFrameAddr->GetFound()) { + const CMIUtilString &rStrFrameAddr(pArgFrameAddr->GetValue()); + bCurrentFrame = CMIUtilString::Compare(rStrFrameAddr, "*"); + if (!bCurrentFrame && (nFrame == UINT64_MAX)) { + // FIXME: *addr isn't implemented. Exit with error if --thread isn't + // specified. + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_OPTION_NOT_FOUND), + m_cmdData.strMiCmd.c_str(), + m_constStrArgFrame.c_str())); + return MIstatus::failure; + } + } + + const CMIUtilString &rStrExpression(pArgExpression->GetValue()); + m_strExpression = rStrExpression; + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); + lldb::SBThread thread = (nThreadId != UINT64_MAX) + ? sbProcess.GetThreadByIndexID(nThreadId) + : sbProcess.GetSelectedThread(); + m_nThreadId = thread.GetIndexID(); + lldb::SBFrame frame = bCurrentFrame ? thread.GetSelectedFrame() + : thread.GetFrameAtIndex(nFrame); + lldb::SBValue value; + + if (rStrExpression[0] == '$') { + const CMIUtilString rStrRegister(rStrExpression.substr(1)); + value = frame.FindRegister(rStrRegister.c_str()); + } else { + const bool bArgs = true; + const bool bLocals = true; + const bool bStatics = true; + const bool bInScopeOnly = true; + const lldb::SBValueList valueList = + frame.GetVariables(bArgs, bLocals, bStatics, bInScopeOnly); + value = valueList.GetFirstValueByName(rStrExpression.c_str()); + } + + if (!value.IsValid()) + value = frame.EvaluateExpression(rStrExpression.c_str()); + + if (value.IsValid() && value.GetError().Success()) { + CompleteSBValue(value); + m_bValid = true; + m_nChildren = value.GetNumChildren(); + m_strType = CMICmnLLDBUtilSBValue(value).GetTypeNameDisplay(); + + // This gets added to CMICmnLLDBDebugSessionInfoVarObj static container of + // varObjs + CMICmnLLDBDebugSessionInfoVarObj varObj(rStrExpression, m_strVarName, + value); + m_strValue = varObj.GetValueFormatted(); + } else { + m_strValue = value.GetError().GetCString(); + } + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarCreate::Acknowledge() { + if (m_bValid) { + // MI print + // "%s^done,name=\"%s\",numchild=\"%d\",value=\"%s\",type=\"%s\",thread-id=\"%llu\",has_more=\"%u\"" + const CMICmnMIValueConst miValueConst(m_strVarName); + CMICmnMIValueResult miValueResultAll("name", miValueConst); + const CMIUtilString strNumChild(CMIUtilString::Format("%d", m_nChildren)); + const CMICmnMIValueConst miValueConst2(strNumChild); + miValueResultAll.Add("numchild", miValueConst2); + const CMICmnMIValueConst miValueConst3(m_strValue); + miValueResultAll.Add("value", miValueConst3); + const CMICmnMIValueConst miValueConst4(m_strType); + miValueResultAll.Add("type", miValueConst4); + const CMIUtilString strThreadId(CMIUtilString::Format("%llu", m_nThreadId)); + const CMICmnMIValueConst miValueConst5(strThreadId); + miValueResultAll.Add("thread-id", miValueConst5); + const CMICmnMIValueConst miValueConst6("0"); + miValueResultAll.Add("has_more", miValueConst6); + + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResultAll); + m_miResultRecord = miRecordResult; + + return MIstatus::success; + } + + CMIUtilString strErrMsg(m_strValue); + if (m_strValue.empty()) + strErrMsg = CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_VARIABLE_CREATION_FAILED), m_strExpression.c_str()); + const CMICmnMIValueConst miValueConst( + strErrMsg.Escape(true /* vbEscapeQuotes */)); + CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdVarCreate::CreateSelf() { + return new CMICmdCmdVarCreate(); +} + +//++ +// Details: Complete SBValue object and its children to get +// SBValue::GetValueDidChange +// work. +// Type: Method. +// Args: vrwValue - (R) Value to update. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +void CMICmdCmdVarCreate::CompleteSBValue(lldb::SBValue &vrwValue) { + // Force a value to update + vrwValue.GetValueDidChange(); + + // And update its children + lldb::SBType valueType = vrwValue.GetType(); + if (!valueType.IsPointerType() && !valueType.IsReferenceType()) { + const MIuint nChildren = vrwValue.GetNumChildren(); + for (MIuint i = 0; i < nChildren; ++i) { + lldb::SBValue member = vrwValue.GetChildAtIndex(i); + if (member.IsValid()) + CompleteSBValue(member); + } + } +} + + +//++ +// Details: CMICmdCmdVarUpdate constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdVarUpdate::CMICmdCmdVarUpdate() + : m_constStrArgPrintValues("print-values"), m_constStrArgName("name"), + m_bValueChanged(false), m_miValueList(true) { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "var-update"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdVarUpdate::CreateSelf; +} + +//++ +// Details: CMICmdCmdVarUpdate destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdVarUpdate::~CMICmdCmdVarUpdate() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarUpdate::ParseArgs() { + m_setCmdArgs.Add( + new CMICmdArgValPrintValues(m_constStrArgPrintValues, false, true)); + m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgName, true, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarUpdate::Execute() { + CMICMDBASE_GETOPTION(pArgPrintValues, PrintValues, m_constStrArgPrintValues); + CMICMDBASE_GETOPTION(pArgName, String, m_constStrArgName); + + CMICmnLLDBDebugSessionInfo::VariableInfoFormat_e eVarInfoFormat = + CMICmnLLDBDebugSessionInfo::eVariableInfoFormat_NoValues; + if (pArgPrintValues->GetFound()) + eVarInfoFormat = + static_cast( + pArgPrintValues->GetValue()); + + const CMIUtilString &rVarObjName(pArgName->GetValue()); + CMICmnLLDBDebugSessionInfoVarObj varObj; + if (!CMICmnLLDBDebugSessionInfoVarObj::VarObjGet(rVarObjName, varObj)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_VARIABLE_DOESNOTEXIST), + m_cmdData.strMiCmd.c_str(), + rVarObjName.c_str())); + return MIstatus::failure; + } + + lldb::SBValue &rValue = varObj.GetValue(); + if (!ExamineSBValueForChange(rValue, m_bValueChanged)) + return MIstatus::failure; + + if (m_bValueChanged) { + varObj.UpdateValue(); + const bool bPrintValue( + (eVarInfoFormat == + CMICmnLLDBDebugSessionInfo::eVariableInfoFormat_AllValues) || + (eVarInfoFormat == + CMICmnLLDBDebugSessionInfo::eVariableInfoFormat_SimpleValues && + rValue.GetNumChildren() == 0)); + const CMIUtilString strValue(bPrintValue ? varObj.GetValueFormatted() : ""); + const CMIUtilString strInScope(rValue.IsInScope() ? "true" : "false"); + MIFormResponse(rVarObjName, bPrintValue ? strValue.c_str() : nullptr, + strInScope); + } + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarUpdate::Acknowledge() { + if (m_bValueChanged) { + // MI print + // "%s^done,changelist=[{name=\"%s\",value=\"%s\",in_scope=\"%s\",type_changed=\"false\",has_more=\"0\"}]" + CMICmnMIValueResult miValueResult("changelist", m_miValueList); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + } else { + // MI print "%s^done,changelist=[]" + const CMICmnMIValueList miValueList(true); + CMICmnMIValueResult miValueResult6("changelist", miValueList); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult6); + m_miResultRecord = miRecordResult; + } + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdVarUpdate::CreateSelf() { + return new CMICmdCmdVarUpdate(); +} + +//++ +// Details: Form the MI response for multiple variables. +// Type: Method. +// Args: vrStrVarName - (R) Session var object's name. +// vpValue - (R) Text version of the value held in the +// variable. +// vrStrScope - (R) In scope "yes" or "no". +// Return: None. +// Throws: None. +//-- +void CMICmdCmdVarUpdate::MIFormResponse(const CMIUtilString &vrStrVarName, + const char *const vpValue, + const CMIUtilString &vrStrScope) { + // MI print + // "[{name=\"%s\",value=\"%s\",in_scope=\"%s\",type_changed=\"false\",has_more=\"0\"}]" + const CMICmnMIValueConst miValueConst(vrStrVarName); + const CMICmnMIValueResult miValueResult("name", miValueConst); + CMICmnMIValueTuple miValueTuple(miValueResult); + if (vpValue != nullptr) { + const CMICmnMIValueConst miValueConst2(vpValue); + const CMICmnMIValueResult miValueResult2("value", miValueConst2); + miValueTuple.Add(miValueResult2); + } + const CMICmnMIValueConst miValueConst3(vrStrScope); + const CMICmnMIValueResult miValueResult3("in_scope", miValueConst3); + miValueTuple.Add(miValueResult3); + const CMICmnMIValueConst miValueConst4("false"); + const CMICmnMIValueResult miValueResult4("type_changed", miValueConst4); + miValueTuple.Add(miValueResult4); + const CMICmnMIValueConst miValueConst5("0"); + const CMICmnMIValueResult miValueResult5("has_more", miValueConst5); + miValueTuple.Add(miValueResult5); + m_miValueList.Add(miValueTuple); +} + +//++ +// Details: Determine if the var object was changed. +// Type: Method. +// Args: vrVarObj - (R) Session var object to examine. +// vrwbChanged - (W) True = The var object was changed, +// False = It was not changed. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarUpdate::ExamineSBValueForChange(lldb::SBValue &vrwValue, + bool &vrwbChanged) { + if (vrwValue.GetValueDidChange()) { + vrwbChanged = true; + return MIstatus::success; + } + + const MIuint nChildren = vrwValue.GetNumChildren(); + for (MIuint i = 0; i < nChildren; ++i) { + lldb::SBValue member = vrwValue.GetChildAtIndex(i); + if (!member.IsValid()) + continue; + + // skip pointers and references to avoid infinite loop + if (member.GetType().GetTypeFlags() & + (lldb::eTypeIsPointer | lldb::eTypeIsReference)) + continue; + + // Handle composite types (i.e. struct or arrays) + if (ExamineSBValueForChange(member, vrwbChanged) && vrwbChanged) + return MIstatus::success; + } + vrwbChanged = false; + return MIstatus::success; +} + + +//++ +// Details: CMICmdCmdVarDelete constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdVarDelete::CMICmdCmdVarDelete() : m_constStrArgName("name") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "var-delete"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdVarDelete::CreateSelf; +} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarDelete::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgName, true, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: CMICmdCmdVarDelete destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdVarDelete::~CMICmdCmdVarDelete() {} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarDelete::Execute() { + CMICMDBASE_GETOPTION(pArgName, String, m_constStrArgName); + + const CMIUtilString &rVarObjName(pArgName->GetValue()); + CMICmnLLDBDebugSessionInfoVarObj::VarObjDelete(rVarObjName); + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarDelete::Acknowledge() { + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdVarDelete::CreateSelf() { + return new CMICmdCmdVarDelete(); +} + + +//++ +// Details: CMICmdCmdVarAssign constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdVarAssign::CMICmdCmdVarAssign() + : m_bOk(true), m_constStrArgName("name"), + m_constStrArgExpression("expression") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "var-assign"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdVarAssign::CreateSelf; +} + +//++ +// Details: CMICmdCmdVarAssign destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdVarAssign::~CMICmdCmdVarAssign() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarAssign::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgName, true, true)); + m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgExpression, true, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarAssign::Execute() { + CMICMDBASE_GETOPTION(pArgName, String, m_constStrArgName); + CMICMDBASE_GETOPTION(pArgExpression, String, m_constStrArgExpression); + + const CMIUtilString &rVarObjName(pArgName->GetValue()); + const CMIUtilString &rExpression(pArgExpression->GetValue()); + + CMICmnLLDBDebugSessionInfoVarObj varObj; + if (!CMICmnLLDBDebugSessionInfoVarObj::VarObjGet(rVarObjName, varObj)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_VARIABLE_DOESNOTEXIST), + m_cmdData.strMiCmd.c_str(), + rVarObjName.c_str())); + return MIstatus::failure; + } + m_varObjName = rVarObjName; + + CMIUtilString strExpression(rExpression.Trim()); + strExpression = strExpression.Trim('"'); + lldb::SBValue &rValue(const_cast(varObj.GetValue())); + m_bOk = rValue.SetValueFromCString(strExpression.c_str()); + if (m_bOk) + varObj.UpdateValue(); + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarAssign::Acknowledge() { + if (m_bOk) { + // MI print "%s^done,value=\"%s\"" + CMICmnLLDBDebugSessionInfoVarObj varObj; + CMICmnLLDBDebugSessionInfoVarObj::VarObjGet(m_varObjName, varObj); + const CMICmnMIValueConst miValueConst(varObj.GetValueFormatted()); + const CMICmnMIValueResult miValueResult("value", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; + } + + const CMICmnMIValueConst miValueConst("expression could not be evaluated"); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdVarAssign::CreateSelf() { + return new CMICmdCmdVarAssign(); +} + + +//++ +// Details: CMICmdCmdVarSetFormat constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdVarSetFormat::CMICmdCmdVarSetFormat() + : m_constStrArgName("name"), m_constStrArgFormatSpec("format-spec") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "var-set-format"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdVarSetFormat::CreateSelf; +} + +//++ +// Details: CMICmdCmdVarSetFormat destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdVarSetFormat::~CMICmdCmdVarSetFormat() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarSetFormat::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgName, true, true)); + m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgFormatSpec, true, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarSetFormat::Execute() { + CMICMDBASE_GETOPTION(pArgName, String, m_constStrArgName); + CMICMDBASE_GETOPTION(pArgFormatSpec, String, m_constStrArgFormatSpec); + + const CMIUtilString &rVarObjName(pArgName->GetValue()); + const CMIUtilString &rExpression(pArgFormatSpec->GetValue()); + + CMICmnLLDBDebugSessionInfoVarObj varObj; + if (!CMICmnLLDBDebugSessionInfoVarObj::VarObjGet(rVarObjName, varObj)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_VARIABLE_DOESNOTEXIST), + m_cmdData.strMiCmd.c_str(), + rVarObjName.c_str())); + return MIstatus::failure; + } + if (!varObj.SetVarFormat( + CMICmnLLDBDebugSessionInfoVarObj::GetVarFormatForString( + rExpression))) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_VARIABLE_ENUM_INVALID), + m_cmdData.strMiCmd.c_str(), + rVarObjName.c_str(), rExpression.c_str())); + return MIstatus::failure; + } + varObj.UpdateValue(); + + m_varObjName = rVarObjName; + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarSetFormat::Acknowledge() { + // MI print + // "%s^done,changelist=[{name=\"%s\",value=\"%s\",in_scope=\"%s\",type_changed=\"false\",has_more=\"0\"}]" + CMICmnLLDBDebugSessionInfoVarObj varObj; + CMICmnLLDBDebugSessionInfoVarObj::VarObjGet(m_varObjName, varObj); + const CMICmnMIValueConst miValueConst(m_varObjName); + const CMICmnMIValueResult miValueResult("name", miValueConst); + CMICmnMIValueTuple miValueTuple(miValueResult); + const CMICmnMIValueConst miValueConst2(varObj.GetValueFormatted()); + const CMICmnMIValueResult miValueResult2("value", miValueConst2); + miValueTuple.Add(miValueResult2); + lldb::SBValue &rValue = const_cast(varObj.GetValue()); + const CMICmnMIValueConst miValueConst3(rValue.IsInScope() ? "true" : "false"); + const CMICmnMIValueResult miValueResult3("in_scope", miValueConst3); + miValueTuple.Add(miValueResult3); + const CMICmnMIValueConst miValueConst4("false"); + const CMICmnMIValueResult miValueResult4("type_changed", miValueConst4); + miValueTuple.Add(miValueResult4); + const CMICmnMIValueConst miValueConst5("0"); + const CMICmnMIValueResult miValueResult5("type_changed", miValueConst5); + miValueTuple.Add(miValueResult5); + const CMICmnMIValueList miValueList(miValueTuple); + const CMICmnMIValueResult miValueResult6("changelist", miValueList); + + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult6); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdVarSetFormat::CreateSelf() { + return new CMICmdCmdVarSetFormat(); +} + + +//++ +// Details: CMICmdCmdVarListChildren constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdVarListChildren::CMICmdCmdVarListChildren() + : m_constStrArgPrintValues("print-values"), m_constStrArgName("name"), + m_constStrArgFrom("from"), m_constStrArgTo("to"), m_bValueValid(false), + m_nChildren(0), m_miValueList(true), m_bHasMore(false) { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "var-list-children"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdVarListChildren::CreateSelf; +} + +//++ +// Details: CMICmdCmdVarListChildren destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdVarListChildren::~CMICmdCmdVarListChildren() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarListChildren::ParseArgs() { + m_setCmdArgs.Add( + new CMICmdArgValPrintValues(m_constStrArgPrintValues, false, true)); + m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgName, true, true, true)); + m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgFrom, false, true)); + m_setCmdArgs.Add(new CMICmdArgValNumber(m_constStrArgTo, false, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarListChildren::Execute() { + CMICMDBASE_GETOPTION(pArgPrintValues, PrintValues, m_constStrArgPrintValues); + CMICMDBASE_GETOPTION(pArgName, String, m_constStrArgName); + CMICMDBASE_GETOPTION(pArgFrom, Number, m_constStrArgFrom); + CMICMDBASE_GETOPTION(pArgTo, Number, m_constStrArgTo); + + CMICmnLLDBDebugSessionInfo::VariableInfoFormat_e eVarInfoFormat = + CMICmnLLDBDebugSessionInfo::eVariableInfoFormat_NoValues; + if (pArgPrintValues->GetFound()) + eVarInfoFormat = + static_cast( + pArgPrintValues->GetValue()); + + const CMIUtilString &rVarObjName(pArgName->GetValue()); + CMICmnLLDBDebugSessionInfoVarObj varObj; + if (!CMICmnLLDBDebugSessionInfoVarObj::VarObjGet(rVarObjName, varObj)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_VARIABLE_DOESNOTEXIST), + m_cmdData.strMiCmd.c_str(), + rVarObjName.c_str())); + return MIstatus::failure; + } + + MIuint nFrom = 0; + MIuint nTo = UINT32_MAX; + if (pArgFrom->GetFound() && pArgTo->GetFound()) { + nFrom = pArgFrom->GetValue(); + nTo = pArgTo->GetValue(); + } else if (pArgFrom->GetFound() || pArgTo->GetFound()) { + // Only from or to was specified but both are required + SetError( + CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_VARIABLE_CHILD_RANGE_INVALID), + m_cmdData.strMiCmd.c_str())); + return MIstatus::failure; + } + + lldb::SBValue &rValue = const_cast(varObj.GetValue()); + m_bValueValid = rValue.IsValid(); + if (!m_bValueValid) + return MIstatus::success; + + const MIuint nChildren = rValue.GetNumChildren(); + m_bHasMore = nTo < nChildren; + nTo = std::min(nTo, nChildren); + m_nChildren = nFrom < nTo ? nTo - nFrom : 0; + for (MIuint i = nFrom; i < nTo; i++) { + lldb::SBValue member = rValue.GetChildAtIndex(i); + const CMICmnLLDBUtilSBValue utilValue(member); + const CMIUtilString strExp = utilValue.GetName(); + const CMIUtilString name( + strExp.empty() ? CMIUtilString::Format("%s.$%u", rVarObjName.c_str(), i) + : CMIUtilString::Format("%s.%s", rVarObjName.c_str(), + strExp.c_str())); + const MIuint nChildren = member.GetNumChildren(); + const CMIUtilString strThreadId( + CMIUtilString::Format("%u", member.GetThread().GetIndexID())); + + // Varobj gets added to CMICmnLLDBDebugSessionInfoVarObj static container of + // varObjs + CMICmnLLDBDebugSessionInfoVarObj var(strExp, name, member, rVarObjName); + + // MI print + // "child={name=\"%s\",exp=\"%s\",numchild=\"%d\",value=\"%s\",type=\"%s\",thread-id=\"%u\",has_more=\"%u\"}" + const CMICmnMIValueConst miValueConst(name); + const CMICmnMIValueResult miValueResult("name", miValueConst); + CMICmnMIValueTuple miValueTuple(miValueResult); + const CMICmnMIValueConst miValueConst2(strExp); + const CMICmnMIValueResult miValueResult2("exp", miValueConst2); + miValueTuple.Add(miValueResult2); + const CMIUtilString strNumChild(CMIUtilString::Format("%u", nChildren)); + const CMICmnMIValueConst miValueConst3(strNumChild); + const CMICmnMIValueResult miValueResult3("numchild", miValueConst3); + miValueTuple.Add(miValueResult3); + const CMICmnMIValueConst miValueConst5(utilValue.GetTypeNameDisplay()); + const CMICmnMIValueResult miValueResult5("type", miValueConst5); + miValueTuple.Add(miValueResult5); + const CMICmnMIValueConst miValueConst6(strThreadId); + const CMICmnMIValueResult miValueResult6("thread-id", miValueConst6); + miValueTuple.Add(miValueResult6); + // nChildren == 0 is used to check for simple values + if (eVarInfoFormat == + CMICmnLLDBDebugSessionInfo::eVariableInfoFormat_AllValues || + (eVarInfoFormat == + CMICmnLLDBDebugSessionInfo::eVariableInfoFormat_SimpleValues && + nChildren == 0)) { + const CMIUtilString strValue( + CMICmnLLDBDebugSessionInfoVarObj::GetValueStringFormatted( + member, CMICmnLLDBDebugSessionInfoVarObj::eVarFormat_Natural)); + const CMICmnMIValueConst miValueConst7(strValue); + const CMICmnMIValueResult miValueResult7("value", miValueConst7); + miValueTuple.Add(miValueResult7); + } + const CMICmnMIValueConst miValueConst8("0"); + const CMICmnMIValueResult miValueResult8("has_more", miValueConst8); + miValueTuple.Add(miValueResult8); + const CMICmnMIValueResult miValueResult9("child", miValueTuple); + m_miValueList.Add(miValueResult9); + } + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarListChildren::Acknowledge() { + if (m_bValueValid) { + // MI print "%s^done,numchild=\"%u\",children=[%s],has_more=\"%d\"" + const CMIUtilString strNumChild(CMIUtilString::Format("%u", m_nChildren)); + const CMICmnMIValueConst miValueConst(strNumChild); + CMICmnMIValueResult miValueResult("numchild", miValueConst); + if (m_nChildren != 0) + miValueResult.Add("children", m_miValueList); + const CMIUtilString strHasMore(m_bHasMore ? "1" : "0"); + const CMICmnMIValueConst miValueConst2(strHasMore); + miValueResult.Add("has_more", miValueConst2); + + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; + } + + // MI print "%s^error,msg=\"variable invalid\"" + const CMICmnMIValueConst miValueConst("variable invalid"); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdVarListChildren::CreateSelf() { + return new CMICmdCmdVarListChildren(); +} + + +//++ +// Details: CMICmdCmdVarEvaluateExpression constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdVarEvaluateExpression::CMICmdCmdVarEvaluateExpression() + : m_bValueValid(true), m_constStrArgFormatSpec("-f"), + m_constStrArgName("name") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "var-evaluate-expression"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdVarEvaluateExpression::CreateSelf; +} + +//++ +// Details: CMICmdCmdVarEvaluateExpression destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdVarEvaluateExpression::~CMICmdCmdVarEvaluateExpression() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarEvaluateExpression::ParseArgs() { + m_setCmdArgs.Add( + new CMICmdArgValOptionShort(m_constStrArgFormatSpec, false, false, + CMICmdArgValListBase::eArgValType_String, 1)); + m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgName, true, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarEvaluateExpression::Execute() { + CMICMDBASE_GETOPTION(pArgName, String, m_constStrArgName); + + const CMIUtilString &rVarObjName(pArgName->GetValue()); + CMICmnLLDBDebugSessionInfoVarObj varObj; + if (!CMICmnLLDBDebugSessionInfoVarObj::VarObjGet(rVarObjName, varObj)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_VARIABLE_DOESNOTEXIST), + m_cmdData.strMiCmd.c_str(), + rVarObjName.c_str())); + return MIstatus::failure; + } + + lldb::SBValue &rValue = const_cast(varObj.GetValue()); + m_bValueValid = rValue.IsValid(); + if (!m_bValueValid) + return MIstatus::success; + + m_varObjName = rVarObjName; + varObj.UpdateValue(); + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarEvaluateExpression::Acknowledge() { + if (m_bValueValid) { + CMICmnLLDBDebugSessionInfoVarObj varObj; + CMICmnLLDBDebugSessionInfoVarObj::VarObjGet(m_varObjName, varObj); + const CMICmnMIValueConst miValueConst(varObj.GetValueFormatted()); + const CMICmnMIValueResult miValueResult("value", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + + const CMICmnMIValueConst miValueConst("variable invalid"); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdVarEvaluateExpression::CreateSelf() { + return new CMICmdCmdVarEvaluateExpression(); +} + + +//++ +// Details: CMICmdCmdVarInfoPathExpression constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdVarInfoPathExpression::CMICmdCmdVarInfoPathExpression() + : m_bValueValid(true), m_constStrArgName("name") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "var-info-path-expression"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdVarInfoPathExpression::CreateSelf; +} + +//++ +// Details: CMICmdCmdVarInfoPathExpression destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdVarInfoPathExpression::~CMICmdCmdVarInfoPathExpression() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarInfoPathExpression::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgName, true, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarInfoPathExpression::Execute() { + CMICMDBASE_GETOPTION(pArgName, String, m_constStrArgName); + + const CMIUtilString &rVarObjName(pArgName->GetValue()); + CMICmnLLDBDebugSessionInfoVarObj varObj; + if (!CMICmnLLDBDebugSessionInfoVarObj::VarObjGet(rVarObjName, varObj)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_VARIABLE_DOESNOTEXIST), + m_cmdData.strMiCmd.c_str(), + rVarObjName.c_str())); + return MIstatus::failure; + } + + lldb::SBValue &rValue = const_cast(varObj.GetValue()); + m_bValueValid = rValue.IsValid(); + if (!m_bValueValid) + return MIstatus::success; + + lldb::SBStream stream; + if (!rValue.GetExpressionPath(stream, true)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_VARIABLE_EXPRESSIONPATH), + m_cmdData.strMiCmd.c_str(), + rVarObjName.c_str())); + return MIstatus::failure; + } + + const char *pPathExpression = stream.GetData(); + if (pPathExpression == nullptr) { + // Build expression from what we do know + m_strPathExpression = varObj.GetNameReal(); + return MIstatus::success; + } + + // Has LLDB returned a var signature of it's own + if (pPathExpression[0] != '$') { + m_strPathExpression = pPathExpression; + return MIstatus::success; + } + + // Build expression from what we do know + const CMIUtilString &rVarParentName(varObj.GetVarParentName()); + if (rVarParentName.empty()) { + m_strPathExpression = varObj.GetNameReal(); + } else { + CMICmnLLDBDebugSessionInfoVarObj varObjParent; + if (!CMICmnLLDBDebugSessionInfoVarObj::VarObjGet(rVarParentName, + varObjParent)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_VARIABLE_DOESNOTEXIST), + m_cmdData.strMiCmd.c_str(), + rVarParentName.c_str())); + return MIstatus::failure; + } + m_strPathExpression = + CMIUtilString::Format("%s.%s", varObjParent.GetNameReal().c_str(), + varObj.GetNameReal().c_str()); + } + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarInfoPathExpression::Acknowledge() { + if (m_bValueValid) { + const CMICmnMIValueConst miValueConst(m_strPathExpression); + const CMICmnMIValueResult miValueResult("path_expr", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + return MIstatus::success; + } + + const CMICmnMIValueConst miValueConst("variable invalid"); + const CMICmnMIValueResult miValueResult("msg", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdVarInfoPathExpression::CreateSelf() { + return new CMICmdCmdVarInfoPathExpression(); +} + + +//++ +// Details: CMICmdCmdVarShowAttributes constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdVarShowAttributes::CMICmdCmdVarShowAttributes() + : m_constStrArgName("name") { + // Command factory matches this name with that received from the stdin stream + m_strMiCmd = "var-show-attributes"; + + // Required by the CMICmdFactory when registering *this command + m_pSelfCreatorFn = &CMICmdCmdVarShowAttributes::CreateSelf; +} + +//++ +// Details: CMICmdCmdVarShowAttributes destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdCmdVarShowAttributes::~CMICmdCmdVarShowAttributes() {} + +//++ +// Details: The invoker requires this function. The parses the command line +// options +// arguments to extract values for each of those arguments. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarShowAttributes::ParseArgs() { + m_setCmdArgs.Add(new CMICmdArgValString(m_constStrArgName, true, true)); + return ParseValidateCmdOptions(); +} + +//++ +// Details: The invoker requires this function. The command does work in this +// function. +// The command is likely to communicate with the LLDB SBDebugger in +// here. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarShowAttributes::Execute() { + CMICMDBASE_GETOPTION(pArgName, String, m_constStrArgName); + + const CMIUtilString &rVarObjName(pArgName->GetValue()); + CMICmnLLDBDebugSessionInfoVarObj varObj; + if (!CMICmnLLDBDebugSessionInfoVarObj::VarObjGet(rVarObjName, varObj)) { + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_VARIABLE_DOESNOTEXIST), + m_cmdData.strMiCmd.c_str(), + rVarObjName.c_str())); + return MIstatus::failure; + } + + return MIstatus::success; +} + +//++ +// Details: The invoker requires this function. The command prepares a MI Record +// Result +// for the work carried out in the Execute(). +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdCmdVarShowAttributes::Acknowledge() { + // MI output: "%s^done,status=\"editable\"]" + const CMICmnMIValueConst miValueConst("editable"); + const CMICmnMIValueResult miValueResult("status", miValueConst); + const CMICmnMIResultRecord miRecordResult( + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, + miValueResult); + m_miResultRecord = miRecordResult; + + return MIstatus::success; +} + +//++ +// Details: Required by the CMICmdFactory when registering *this command. The +// factory +// calls this function to create an instance of *this command. +// Type: Static method. +// Args: None. +// Return: CMICmdBase * - Pointer to a new command. +// Throws: None. +//-- +CMICmdBase *CMICmdCmdVarShowAttributes::CreateSelf() { + return new CMICmdCmdVarShowAttributes(); +} diff --git a/src/MICmdCmdVar.h b/src/MICmdCmdVar.h new file mode 100644 index 00000000000..cdd036688ca --- /dev/null +++ b/src/MICmdCmdVar.h @@ -0,0 +1,348 @@ +//===-- MICmdCmdVar.h -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: CMICmdCmdVarCreate interface. +// CMICmdCmdVarUpdate interface. +// CMICmdCmdVarDelete interface. +// CMICmdCmdVarAssign interface. +// CMICmdCmdVarSetFormat interface. +// CMICmdCmdVarListChildren interface. +// CMICmdCmdVarEvaluateExpression interface. +// CMICmdCmdVarInfoPathExpression interface. +// CMICmdCmdVarShowAttributes interface. +// +// To implement new MI commands derive a new command class from the +// command base +// class. To enable the new command for interpretation add the new +// command class +// to the command factory. The files of relevance are: +// MICmdCommands.cpp +// MICmdBase.h / .cpp +// MICmdCmd.h / .cpp +// For an introduction to adding a new command see +// CMICmdCmdSupportInfoMiCmdQuery +// command class as an example. + +#pragma once + +// In-house headers: +#include "MICmdBase.h" +#include "MICmnLLDBDebugSessionInfo.h" +#include "MICmnLLDBDebugSessionInfoVarObj.h" +#include "MICmnMIValueList.h" +#include "MICmnMIValueTuple.h" + +// Declarations: +class CMICmnLLDBDebugSessionInfoVarObj; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "var-create". +//-- +class CMICmdCmdVarCreate : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdVarCreate(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + + // Overridden: +public: + // From CMICmnBase + /* dtor */ ~CMICmdCmdVarCreate() override; + + // Methods: +private: + void CompleteSBValue(lldb::SBValue &vrwValue); + + // Attribute: +private: + CMIUtilString m_strVarName; + MIuint m_nChildren; + MIuint64 m_nThreadId; + CMIUtilString m_strType; + bool m_bValid; // True = Variable is valid, false = not valid + CMIUtilString m_strExpression; + CMIUtilString m_strValue; + const CMIUtilString m_constStrArgName; + const CMIUtilString m_constStrArgFrameAddr; + const CMIUtilString m_constStrArgExpression; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "var-update". +//-- +class CMICmdCmdVarUpdate : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdVarUpdate(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + + // Overridden: +public: + // From CMICmnBase + /* dtor */ ~CMICmdCmdVarUpdate() override; + + // Methods: +private: + bool ExamineSBValueForChange(lldb::SBValue &vrwValue, bool &vrwbChanged); + void MIFormResponse(const CMIUtilString &vrStrVarName, + const char *const vpValue, + const CMIUtilString &vrStrScope); + + // Attribute: +private: + const CMIUtilString m_constStrArgPrintValues; + const CMIUtilString m_constStrArgName; + bool m_bValueChanged; // True = yes value changed, false = no change + CMICmnMIValueList m_miValueList; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "var-delete". +//-- +class CMICmdCmdVarDelete : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdVarDelete(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdVarDelete() override; + + // Attribute: +private: + const CMIUtilString m_constStrArgName; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "var-assign". +//-- +class CMICmdCmdVarAssign : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdVarAssign(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdVarAssign() override; + + // Attributes: +private: + bool m_bOk; // True = success, false = failure + CMIUtilString m_varObjName; + const CMIUtilString m_constStrArgName; + const CMIUtilString m_constStrArgExpression; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "var-set-format". +//-- +class CMICmdCmdVarSetFormat : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdVarSetFormat(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdVarSetFormat() override; + + // Attributes: +private: + CMIUtilString m_varObjName; + const CMIUtilString m_constStrArgName; + const CMIUtilString m_constStrArgFormatSpec; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "var-list-children". +//-- +class CMICmdCmdVarListChildren : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdVarListChildren(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdVarListChildren() override; + + // Attributes: +private: + const CMIUtilString m_constStrArgPrintValues; + const CMIUtilString m_constStrArgName; + const CMIUtilString m_constStrArgFrom; + const CMIUtilString m_constStrArgTo; + bool m_bValueValid; // True = yes SBValue object is valid, false = not valid + MIuint m_nChildren; + CMICmnMIValueList m_miValueList; + bool m_bHasMore; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "var-evaluate-expression". +//-- +class CMICmdCmdVarEvaluateExpression : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdVarEvaluateExpression(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdVarEvaluateExpression() override; + + // Attributes: +private: + bool m_bValueValid; // True = yes SBValue object is valid, false = not valid + CMIUtilString m_varObjName; + const CMIUtilString m_constStrArgFormatSpec; // Not handled by *this command + const CMIUtilString m_constStrArgName; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "var-info-path-expression". +//-- +class CMICmdCmdVarInfoPathExpression : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdVarInfoPathExpression(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdVarInfoPathExpression() override; + + // Attributes: +private: + bool m_bValueValid; // True = yes SBValue object is valid, false = not valid + CMIUtilString m_strPathExpression; + const CMIUtilString m_constStrArgName; +}; + +//++ +//============================================================================ +// Details: MI command class. MI commands derived from the command base class. +// *this class implements MI command "var-show-attributes". +//-- +class CMICmdCmdVarShowAttributes : public CMICmdBase { + // Statics: +public: + // Required by the CMICmdFactory when registering *this command + static CMICmdBase *CreateSelf(); + + // Methods: +public: + /* ctor */ CMICmdCmdVarShowAttributes(); + + // Overridden: +public: + // From CMICmdInvoker::ICmd + bool Execute() override; + bool Acknowledge() override; + bool ParseArgs() override; + // From CMICmnBase + /* dtor */ ~CMICmdCmdVarShowAttributes() override; + + // Attributes: +private: + const CMIUtilString m_constStrArgName; +}; diff --git a/src/MICmdCommands.cpp b/src/MICmdCommands.cpp new file mode 100644 index 00000000000..cffc50a9ba6 --- /dev/null +++ b/src/MICmdCommands.cpp @@ -0,0 +1,134 @@ +//===-- MICmdCommands.cpp ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: MI command are registered with the MI command factory. +// +// To implement new MI commands derive a new command class from the +// command base +// class. To enable the new command for interpretation add the new +// command class +// to the command factory. The files of relevance are: +// MICmdCommands.cpp +// MICmdBase.h / .cpp +// MICmdCmd.h / .cpp + +// In-house headers: +#include "MICmdCommands.h" +#include "MICmdCmd.h" +#include "MICmdCmdBreak.h" +#include "MICmdCmdData.h" +#include "MICmdCmdEnviro.h" +#include "MICmdCmdExec.h" +#include "MICmdCmdFile.h" +#include "MICmdCmdGdbInfo.h" +#include "MICmdCmdGdbSet.h" +#include "MICmdCmdGdbShow.h" +#include "MICmdCmdGdbThread.h" +#include "MICmdCmdMiscellanous.h" +#include "MICmdCmdStack.h" +#include "MICmdCmdSupportInfo.h" +#include "MICmdCmdSupportList.h" +#include "MICmdCmdSymbol.h" +#include "MICmdCmdTarget.h" +#include "MICmdCmdThread.h" +#include "MICmdCmdTrace.h" +#include "MICmdCmdVar.h" +#include "MICmdFactory.h" + +namespace MICmnCommands { +template static bool Register(); +} + +//++ +// Details: Command to command factory registration function. +// Type: Template function. +// Args: typename T - A command type class. +// Return: bool - True = yes command is registered, false = command failed to +// register. +// Throws: None. +//-- +template static bool MICmnCommands::Register() { + static CMICmdFactory &rCmdFactory = CMICmdFactory::Instance(); + const CMIUtilString strMiCmd = T().GetMiCmd(); + CMICmdFactory::CmdCreatorFnPtr fn = T().GetCmdCreatorFn(); + return rCmdFactory.CmdRegister(strMiCmd, fn); +} + +//++ +// Details: Register commands with MI command factory +// Type: Function. +// Args: None. +// Return: bool - True = yes all commands are registered, +// false = one or more commands failed to register. +// Throws: None. +//-- +bool MICmnCommands::RegisterAll() { + bool bOk = MIstatus::success; + + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + bOk &= Register(); + + return bOk; +} diff --git a/src/MICmdCommands.h b/src/MICmdCommands.h new file mode 100644 index 00000000000..f2e3bfde760 --- /dev/null +++ b/src/MICmdCommands.h @@ -0,0 +1,19 @@ +//===-- MICmdCommands.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +namespace MICmnCommands { + +//++ +//============================================================================ +// Details: MI Command are instantiated and registered automatically with the +// Command Factory +//-- +bool RegisterAll(); +} diff --git a/src/MICmdData.cpp b/src/MICmdData.cpp new file mode 100644 index 00000000000..6d0b679b857 --- /dev/null +++ b/src/MICmdData.cpp @@ -0,0 +1,10 @@ +//===-- MICmdData.cpp -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmdData.h" diff --git a/src/MICmdData.h b/src/MICmdData.h new file mode 100644 index 00000000000..3e46b54d835 --- /dev/null +++ b/src/MICmdData.h @@ -0,0 +1,58 @@ +//===-- MICmdData.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmnResources.h" + +//++ +//============================================================================ +// Details: MI command metadata. Holds the command's name, MI number and options +// as found on stdin. Holds the command's MI output (written to +// stdout). +//-- +struct SMICmdData { + SMICmdData() + : id(0), bCmdValid(false), bCmdExecutedSuccessfully(false), + bMIOldStyle(false), bHasResultRecordExtra(false) {} + + MIuint id; // A command's unique ID i.e. GUID + CMIUtilString strMiCmdToken; // The command's MI token (a number) + CMIUtilString strMiCmd; // The command's name + CMIUtilString strMiCmdOption; // The command's arguments or options + CMIUtilString strMiCmdAll; // The text as received from the client + CMIUtilString + strMiCmdResultRecord; // Each command forms 1 response to its input + CMIUtilString strMiCmdResultRecordExtra; // Hack command produce more response + // text to help the client because of + // using LLDB + bool bCmdValid; // True = Valid MI format command, false = invalid + bool bCmdExecutedSuccessfully; // True = Command finished successfully, false + // = Did not start/did not complete + CMIUtilString strErrorDescription; // Command failed this is why + bool bMIOldStyle; // True = format "3thread", false = format "3-thread" + bool bHasResultRecordExtra; // True = Yes command produced additional MI + // output to its 1 line response, false = no extra + // MI output formed + + void Clear() { + id = 0; + strMiCmdToken.clear(); + strMiCmd = MIRSRC(IDS_CMD_ERR_CMD_RUN_BUT_NO_ACTION); + strMiCmdOption.clear(); + strMiCmdAll.clear(); + strMiCmdResultRecord.clear(); + strMiCmdResultRecordExtra.clear(); + bCmdValid = false; + bCmdExecutedSuccessfully = false; + strErrorDescription.clear(); + bMIOldStyle = false; + bHasResultRecordExtra = false; + } +}; diff --git a/src/MICmdFactory.cpp b/src/MICmdFactory.cpp new file mode 100644 index 00000000000..30995835c68 --- /dev/null +++ b/src/MICmdFactory.cpp @@ -0,0 +1,206 @@ +//===-- MICmdFactory.cpp ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmdFactory.h" +#include "MICmdBase.h" +#include "MICmdCommands.h" +#include "MICmdData.h" +#include "MICmnResources.h" + +//++ +// Details: CMICmdFactory constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdFactory::CMICmdFactory() {} + +//++ +// Details: CMICmdFactory destructor. +// Type: Overridable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdFactory::~CMICmdFactory() { Shutdown(); } + +//++ +// Details: Initialize resources for *this Command factory. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmdFactory::Initialize() { + m_clientUsageRefCnt++; + + if (m_bInitialized) + return MIstatus::success; + + m_bInitialized = true; + + MICmnCommands::RegisterAll(); + + return MIstatus::success; +} + +//++ +// Details: Release resources for *this Command Factory. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmdFactory::Shutdown() { + if (--m_clientUsageRefCnt > 0) + return MIstatus::success; + + if (!m_bInitialized) + return MIstatus::success; + + m_mapMiCmdToCmdCreatorFn.clear(); + + m_bInitialized = false; + + return MIstatus::success; +} + +//++ +// Details: Register a command's creator function with the command identifier +// the MI +// command name i.e. 'file-exec-and-symbols'. +// Type: Method. +// Args: vMiCmd - (R) Command's name, the MI command. +// vCmdCreateFn - (R) Command's creator function pointer. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmdFactory::CmdRegister(const CMIUtilString &vMiCmd, + CmdCreatorFnPtr vCmdCreateFn) { + if (!IsValid(vMiCmd)) { + SetErrorDescription(CMIUtilString::Format( + MIRSRC(IDS_CMDFACTORY_ERR_INVALID_CMD_NAME), vMiCmd.c_str())); + return MIstatus::failure; + } + if (vCmdCreateFn == nullptr) { + SetErrorDescription(CMIUtilString::Format( + MIRSRC(IDS_CMDFACTORY_ERR_INVALID_CMD_CR8FN), vMiCmd.c_str())); + return MIstatus::failure; + } + + if (HaveAlready(vMiCmd)) { + SetErrorDescription(CMIUtilString::Format( + MIRSRC(IDS_CMDFACTORY_ERR_CMD_ALREADY_REGED), vMiCmd.c_str())); + return MIstatus::failure; + } + + MapPairMiCmdToCmdCreatorFn_t pr(vMiCmd, vCmdCreateFn); + m_mapMiCmdToCmdCreatorFn.insert(pr); + + return MIstatus::success; +} + +//++ +// Details: Check a command is already registered. +// Type: Method. +// Args: vMiCmd - (R) Command's name, the MI command. +// Return: True - registered. +// False - not found. +// Throws: None. +//-- +bool CMICmdFactory::HaveAlready(const CMIUtilString &vMiCmd) const { + const MapMiCmdToCmdCreatorFn_t::const_iterator it = + m_mapMiCmdToCmdCreatorFn.find(vMiCmd); + return it != m_mapMiCmdToCmdCreatorFn.end(); +} + +//++ +// Details: Check a command's name is valid: +// - name is not empty +// - name does not have spaces +// Type: Method. +// Args: vMiCmd - (R) Command's name, the MI command. +// Return: True - valid. +// False - not valid. +// Throws: None. +//-- +bool CMICmdFactory::IsValid(const CMIUtilString &vMiCmd) const { + bool bValid = true; + + if (vMiCmd.empty()) { + bValid = false; + return false; + } + + const size_t nPos = vMiCmd.find(' '); + if (nPos != std::string::npos) + bValid = false; + + return bValid; +} + +//++ +// Details: Check a command is already registered. +// Type: Method. +// Args: vMiCmd - (R) Command's name, the MI command. +// Return: True - registered. +// False - not found. +// Throws: None. +//-- +bool CMICmdFactory::CmdExist(const CMIUtilString &vMiCmd) const { + return HaveAlready(vMiCmd); +} + +//++ +// Details: Create a command given the specified MI command name. The command +// data object +// contains the options for the command. +// Type: Method. +// Args: vMiCmd - (R) Command's name, the MI command. +// vCmdData - (RW) Command's metadata status/information/result +// object. +// vpNewCmd - (W) New command instance. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmdFactory::CmdCreate(const CMIUtilString &vMiCmd, + const SMICmdData &vCmdData, + CMICmdBase *&vpNewCmd) { + vpNewCmd = nullptr; + + if (!IsValid(vMiCmd)) { + SetErrorDescription(CMIUtilString::Format( + MIRSRC(IDS_CMDFACTORY_ERR_INVALID_CMD_NAME), vMiCmd.c_str())); + return MIstatus::failure; + } + if (!HaveAlready(vMiCmd)) { + SetErrorDescription(CMIUtilString::Format( + MIRSRC(IDS_CMDFACTORY_ERR_CMD_NOT_REGISTERED), vMiCmd.c_str())); + return MIstatus::failure; + } + + const MapMiCmdToCmdCreatorFn_t::const_iterator it = + m_mapMiCmdToCmdCreatorFn.find(vMiCmd); + const CMIUtilString &rMiCmd((*it).first); + MIunused(rMiCmd); + CmdCreatorFnPtr pFn = (*it).second; + CMICmdBase *pCmd = (*pFn)(); + + SMICmdData cmdData(vCmdData); + cmdData.id = pCmd->GetGUID(); + pCmd->SetCmdData(cmdData); + vpNewCmd = pCmd; + + return MIstatus::success; +} diff --git a/src/MICmdFactory.h b/src/MICmdFactory.h new file mode 100644 index 00000000000..19987c281ad --- /dev/null +++ b/src/MICmdFactory.h @@ -0,0 +1,84 @@ +//===-- MICmdFactory.h ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// Third party headers +#include + +// In-house headers: +#include "MICmnBase.h" +#include "MIUtilSingletonBase.h" + +// Declarations: +class CMICmdBase; +struct SMICmdData; + +//++ +//============================================================================ +// Details: MI Command Factory. Holds a list of registered MI commands that +// MI application understands to interpret. Creates commands objects. +// The Command Factory is carried out in the main thread. +// A singleton class. +//-- +class CMICmdFactory : public CMICmnBase, public MI::ISingleton { + friend class MI::ISingleton; + + // Typedefs: +public: + typedef CMICmdBase *(*CmdCreatorFnPtr)(); + + // Class: +public: + //++ + // Description: Command's factory's interface for commands to implement. + //-- + class ICmd { + public: + virtual const CMIUtilString &GetMiCmd() const = 0; + virtual CmdCreatorFnPtr GetCmdCreatorFn() const = 0; + // virtual CMICmdBase * CreateSelf( void ) = 0; // Not + // possible as require a static creator + // function in the command class, here for awareness + + /* dtor */ virtual ~ICmd() {} + }; + + // Methods: +public: + bool Initialize() override; + bool Shutdown() override; + bool CmdRegister(const CMIUtilString &vMiCmd, CmdCreatorFnPtr vCmdCreateFn); + bool CmdCreate(const CMIUtilString &vMiCmd, const SMICmdData &vCmdData, + CMICmdBase *&vpNewCmd); + bool CmdExist(const CMIUtilString &vMiCmd) const; + + // Methods: +private: + /* ctor */ CMICmdFactory(); + /* ctor */ CMICmdFactory(const CMICmdFactory &); + void operator=(const CMICmdFactory &); + + bool HaveAlready(const CMIUtilString &vMiCmd) const; + bool IsValid(const CMIUtilString &vMiCmd) const; + + // Overridden: +private: + // From CMICmnBase + /* dtor */ ~CMICmdFactory() override; + + // Typedefs: +private: + typedef std::map MapMiCmdToCmdCreatorFn_t; + typedef std::pair + MapPairMiCmdToCmdCreatorFn_t; + + // Attributes: +private: + MapMiCmdToCmdCreatorFn_t m_mapMiCmdToCmdCreatorFn; +}; diff --git a/src/MICmdInterpreter.cpp b/src/MICmdInterpreter.cpp new file mode 100644 index 00000000000..9e37ac2d610 --- /dev/null +++ b/src/MICmdInterpreter.cpp @@ -0,0 +1,290 @@ +//===-- MICmdInterpreter.cpp ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmdInterpreter.h" +#include "MICmdFactory.h" + +//++ +// Details: CMICmdInterpreter constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdInterpreter::CMICmdInterpreter() + : m_rCmdFactory(CMICmdFactory::Instance()) {} + +//++ +// Details: CMICmdInterpreter destructor. +// Type: Overridable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdInterpreter::~CMICmdInterpreter() { Shutdown(); } + +//++ +// Details: Initialize resources for *this Command Interpreter. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdInterpreter::Initialize() { + m_clientUsageRefCnt++; + + if (m_bInitialized) + return MIstatus::success; + + m_bInitialized = true; + + return MIstatus::success; +} + +//++ +// Details: Release resources for *this Command Interpreter. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdInterpreter::Shutdown() { + if (--m_clientUsageRefCnt > 0) + return MIstatus::success; + + if (!m_bInitialized) + return MIstatus::success; + + m_bInitialized = false; + + return MIstatus::success; +} + +//++ +// Details: Establish whether the text data is an MI format type command. +// Type: Method. +// Args: vTextLine - (R) Text data to interpret. +// vwbYesValid - (W) True = MI type command, false = not +// recognised. +// vwbCmdNotInCmdFactor - (W) True = MI command not found in the +// command factory, false = recognised. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdInterpreter::ValidateIsMi(const CMIUtilString &vTextLine, + bool &vwbYesValid, + bool &vwbCmdNotInCmdFactor, + SMICmdData &rwCmdData) { + vwbYesValid = false; + vwbCmdNotInCmdFactor = false; + rwCmdData.Clear(); + + if (vTextLine.empty()) + return MIstatus::success; + + // MI format is [cmd #]-[command name][command arg(s)] + // i.e. 1-file-exec-and-symbols --thread-group i1 DEVICE_EXECUTABLE + // 5-data-evaluate-expression --thread 1 --frame 0 *(argv) + + m_miCmdData.Clear(); + m_miCmdData.strMiCmd = vTextLine; + + // The following change m_miCmdData as valid parts are identified + vwbYesValid = (MiHasCmdTokenEndingHyphen(vTextLine) || + MiHasCmdTokenEndingAlpha(vTextLine)); + vwbYesValid = vwbYesValid && MiHasCmd(vTextLine); + if (vwbYesValid) { + vwbCmdNotInCmdFactor = !HasCmdFactoryGotMiCmd(MiGetCmdData()); + vwbYesValid = !vwbCmdNotInCmdFactor; + } + + // Update command's meta data valid state + m_miCmdData.bCmdValid = vwbYesValid; + + // Ok to return new updated command information + rwCmdData = MiGetCmdData(); + + return MIstatus::success; +} + +//++ +// Details: Establish whether the command name entered on the stdin stream is +// recognised by +// the MI driver. +// Type: Method. +// Args: vCmd - (R) Command information structure. +// Return: bool - True = yes command is recognised, false = command not +// recognised. +// Throws: None. +//-- +bool CMICmdInterpreter::HasCmdFactoryGotMiCmd(const SMICmdData &vCmd) const { + return m_rCmdFactory.CmdExist(vCmd.strMiCmd); +} + +//++ +// Details: Does the command entered match the criteria for a MI command format. +// The format to validate against is 'nn-' where there can be 1 to n +// digits. +// I.e. '2-gdb-exit'. +// Is the execution token present? The command token is entered into +// the +// command meta data structure whether correct or not for reporting or +// later +// command execution purposes. +// Type: Method. +// Args: vTextLine - (R) Text data to interpret. +// Return: bool - True = yes command token present, false = command not +// recognised. +// Throws: None. +//-- +bool CMICmdInterpreter::MiHasCmdTokenEndingHyphen( + const CMIUtilString &vTextLine) { + // The hyphen is mandatory + const size_t nPos = vTextLine.find('-', 0); + if ((nPos == std::string::npos)) + return false; + + if (MiHasCmdTokenPresent(vTextLine)) { + const std::string strNum = vTextLine.substr(0, nPos); + if (!CMIUtilString(strNum).IsNumber()) + return false; + + m_miCmdData.strMiCmdToken = strNum; + } + + m_miCmdData.bMIOldStyle = false; + + return true; +} + +//++ +// Details: Does the command entered match the criteria for a MI command format. +// The format to validate against is 'nnA' where there can be 1 to n +// digits. +// 'A' represents any non numeric token. I.e. '1source .gdbinit'. +// Is the execution token present? The command token is entered into +// the +// command meta data structure whether correct or not for reporting or +// later +// command execution purposes. +// Type: Method. +// Args: vTextLine - (R) Text data to interpret. +// Return: bool - True = yes command token present, false = command not +// recognised. +// Throws: None. +//-- +bool CMICmdInterpreter::MiHasCmdTokenEndingAlpha( + const CMIUtilString &vTextLine) { + char cChar = vTextLine[0]; + MIuint i = 0; + while (::isdigit(cChar) != 0) { + cChar = vTextLine[++i]; + } + if (::isalpha(cChar) == 0) + return false; + if (i == 0) + return false; + + const std::string strNum = vTextLine.substr(0, i); + m_miCmdData.strMiCmdToken = strNum.c_str(); + m_miCmdData.bMIOldStyle = true; + + return true; +} + +//++ +// Details: Does the command entered match the criteria for a MI command format. +// Is the command token present before the hyphen? +// Type: Method. +// Args: vTextLine - (R) Text data to interpret. +// Return: bool - True = yes command token present, false = token not present. +// Throws: None. +//-- +bool CMICmdInterpreter::MiHasCmdTokenPresent(const CMIUtilString &vTextLine) { + const size_t nPos = vTextLine.find('-', 0); + return (nPos > 0); +} + +//++ +// Details: Does the command name entered match the criteria for a MI command +// format. +// Is a recognised command present? The command name is entered into +// the +// command meta data structure whether correct or not for reporting or +// later +// command execution purposes. Command options is present are also put +// into the +// command meta data structure. +// Type: Method. +// Args: vTextLine - (R) Command information structure. +// Return: bool - True = yes command name present, false = command not +// recognised. +// Throws: None. +//-- +bool CMICmdInterpreter::MiHasCmd(const CMIUtilString &vTextLine) { + size_t nPos = 0; + if (m_miCmdData.bMIOldStyle) { + char cChar = vTextLine[0]; + size_t i = 0; + while (::isdigit(cChar) != 0) { + cChar = vTextLine[++i]; + } + nPos = --i; + } else { + nPos = vTextLine.find('-', 0); + } + + bool bFoundCmd = false; + const size_t nLen = vTextLine.length(); + const size_t nPos2 = vTextLine.find(' ', nPos); + if (nPos2 != std::string::npos) { + if (nPos2 == nLen) + return false; + const CMIUtilString cmd = + CMIUtilString(vTextLine.substr(nPos + 1, nPos2 - nPos - 1)); + if (cmd.empty()) + return false; + + m_miCmdData.strMiCmd = cmd; + + if (nPos2 < nLen) + m_miCmdData.strMiCmdOption = + CMIUtilString(vTextLine.substr(nPos2 + 1, nLen - nPos2 - 1)); + + bFoundCmd = true; + } else { + const CMIUtilString cmd = + CMIUtilString(vTextLine.substr(nPos + 1, nLen - nPos - 1)); + if (cmd.empty()) + return false; + m_miCmdData.strMiCmd = cmd; + bFoundCmd = true; + } + + if (bFoundCmd) + m_miCmdData.strMiCmdAll = vTextLine; + + return bFoundCmd; +} + +//++ +// Details: Retrieve the just entered new command from stdin. It contains the +// command +// name, number and any options. +// Type: Method. +// Args: vTextLine - (R) Command information structure. +// Return: SMICmdData & - Command meta data information/result/status. +// Throws: None. +//-- +const SMICmdData &CMICmdInterpreter::MiGetCmdData() const { + return m_miCmdData; +} diff --git a/src/MICmdInterpreter.h b/src/MICmdInterpreter.h new file mode 100644 index 00000000000..100d309ac77 --- /dev/null +++ b/src/MICmdInterpreter.h @@ -0,0 +1,62 @@ +//===-- MICmdInterpreter.h --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmdData.h" +#include "MICmnBase.h" +#include "MIUtilSingletonBase.h" + +// Declarations: +class CMICmdFactory; + +//++ +//============================================================================ +// Details: MI command interpreter. It takes text data from the MI driver +// (which got it from Stdin singleton) and validate the text to see if +// matches Machine Interface (MI) format and commands defined in the +// MI application. +// A singleton class. +//-- +class CMICmdInterpreter : public CMICmnBase, + public MI::ISingleton { + friend MI::ISingleton; + + // Methods: +public: + // Methods: +public: + bool Initialize() override; + bool Shutdown() override; + bool ValidateIsMi(const CMIUtilString &vTextLine, bool &vwbYesValid, + bool &vwbCmdNotInCmdFactor, SMICmdData &rwCmdData); + + // Methods: +private: + /* ctor */ CMICmdInterpreter(); + /* ctor */ CMICmdInterpreter(const CMICmdInterpreter &); + void operator=(const CMICmdInterpreter &); + + bool HasCmdFactoryGotMiCmd(const SMICmdData &vCmdData) const; + bool MiHasCmdTokenEndingHyphen(const CMIUtilString &vTextLine); + bool MiHasCmdTokenEndingAlpha(const CMIUtilString &vTextLine); + bool MiHasCmd(const CMIUtilString &vTextLine); + bool MiHasCmdTokenPresent(const CMIUtilString &vTextLine); + const SMICmdData &MiGetCmdData() const; + + // Overridden: +private: + // From CMICmnBase + /* dtor */ ~CMICmdInterpreter() override; + + // Attributes: +private: + SMICmdData m_miCmdData; // Filled in on each new line being interpreted + CMICmdFactory &m_rCmdFactory; +}; diff --git a/src/MICmdInvoker.cpp b/src/MICmdInvoker.cpp new file mode 100644 index 00000000000..f4150a20ccd --- /dev/null +++ b/src/MICmdInvoker.cpp @@ -0,0 +1,321 @@ +//===-- MICmdInvoker.cpp ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmdInvoker.h" +#include "MICmdBase.h" +#include "MICmdMgr.h" +#include "MICmnLog.h" +#include "MICmnStreamStdout.h" +#include "MIDriver.h" + +//++ +// Details: CMICmdInvoker constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdInvoker::CMICmdInvoker() : m_rStreamOut(CMICmnStreamStdout::Instance()) {} + +//++ +// Details: CMICmdInvoker destructor. +// Type: Overridable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdInvoker::~CMICmdInvoker() { Shutdown(); } + +//++ +// Details: Initialize resources for *this Command Invoker. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdInvoker::Initialize() { + m_clientUsageRefCnt++; + + if (m_bInitialized) + return MIstatus::success; + + m_bInitialized = true; + + return MIstatus::success; +} + +//++ +// Details: Release resources for *this Stdin stream. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdInvoker::Shutdown() { + if (--m_clientUsageRefCnt > 0) + return MIstatus::success; + + if (!m_bInitialized) + return MIstatus::success; + + CmdDeleteAll(); + + m_bInitialized = false; + + return MIstatus::success; +} + +//++ +// Details: Empty the map of invoked commands doing work. Command objects are +// deleted too. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMICmdInvoker::CmdDeleteAll() { + CMICmdMgr &rMgr = CMICmdMgr::Instance(); + MapCmdIdToCmd_t::const_iterator it = m_mapCmdIdToCmd.begin(); + while (it != m_mapCmdIdToCmd.end()) { + const MIuint cmdId((*it).first); + MIunused(cmdId); + CMICmdBase *pCmd = (*it).second; + const CMIUtilString &rCmdName(pCmd->GetCmdData().strMiCmd); + MIunused(rCmdName); + rMgr.CmdDelete(pCmd->GetCmdData()); + + // Next + ++it; + } + m_mapCmdIdToCmd.clear(); +} + +//++ +// Details: Remove from the map of invoked commands doing work a command that +// has finished +// its work. The command object is deleted too. +// Type: Method. +// Args: vId - (R) Command object's unique ID. +// vbYesDeleteCmd - (R) True = Delete command object, false = delete +// via the Command Manager. +// Return: None. +// Throws: None. +//-- +bool CMICmdInvoker::CmdDelete(const MIuint vId, + const bool vbYesDeleteCmd /*= false*/) { + CMICmdMgr &rMgr = CMICmdMgr::Instance(); + MapCmdIdToCmd_t::const_iterator it = m_mapCmdIdToCmd.find(vId); + if (it != m_mapCmdIdToCmd.end()) { + CMICmdBase *pCmd = (*it).second; + if (vbYesDeleteCmd) { + // Via registered interest command manager callback *this object to delete + // the command + m_mapCmdIdToCmd.erase(it); + delete pCmd; + } else + // Notify other interested object of this command's pending deletion + rMgr.CmdDelete(pCmd->GetCmdData()); + } + + if (m_mapCmdIdToCmd.empty()) + rMgr.CmdUnregisterForDeleteNotification(*this); + + return MIstatus::success; +} + +//++ +// Details: Add to the map of invoked commands doing work a command that is +// about to +// start to do work. +// Type: Method. +// Args: vCmd - (R) Command object. +// Return: None. +// Throws: None. +//-- +bool CMICmdInvoker::CmdAdd(const CMICmdBase &vCmd) { + if (m_mapCmdIdToCmd.empty()) { + CMICmdMgr &rMgr = CMICmdMgr::Instance(); + rMgr.CmdRegisterForDeleteNotification(*this); + } + + const MIuint &cmdId(vCmd.GetCmdData().id); + MapCmdIdToCmd_t::const_iterator it = m_mapCmdIdToCmd.find(cmdId); + if (it != m_mapCmdIdToCmd.end()) + return MIstatus::success; + + MapPairCmdIdToCmd_t pr(cmdId, const_cast(&vCmd)); + m_mapCmdIdToCmd.insert(pr); + + return MIstatus::success; +} + +//++ +// Details: Having previously had the potential command validated and found +// valid now +// get the command executed. +// If the Functionality returns MIstatus::failure call +// GetErrorDescription(). +// This function is used by the application's main thread. +// Type: Method. +// Args: vCmd - (RW) Command object. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmdInvoker::CmdExecute(CMICmdBase &vCmd) { + bool bOk = CmdAdd(vCmd); + + if (bOk) { + vCmd.AddCommonArgs(); + if (!vCmd.ParseArgs()) { + // Report command execution failed + const SMICmdData cmdData(vCmd.GetCmdData()); + CmdStdout(cmdData); + CmdCauseAppExit(vCmd); + CmdDelete(cmdData.id); + + // Proceed to wait or execute next command + return MIstatus::success; + } + } + + if (bOk && !vCmd.Execute()) { + // Report command execution failed + const SMICmdData cmdData(vCmd.GetCmdData()); + CmdStdout(cmdData); + CmdCauseAppExit(vCmd); + CmdDelete(cmdData.id); + + // Proceed to wait or execute next command + return MIstatus::success; + } + + bOk = CmdExecuteFinished(vCmd); + + return bOk; +} + +//++ +// Details: Called when a command has finished its Execution() work either +// synchronously +// because the command executed was the type a non event type or +// asynchronously +// via the command's callback (because of an SB Listener event). Needs +// to be called +// so that *this invoker call do some house keeping and then proceed to +// call +// the command's Acknowledge() function. +// Type: Method. +// Args: vCmd - (R) Command object. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmdInvoker::CmdExecuteFinished(CMICmdBase &vCmd) { + // Command finished now get the command to gather it's information and form + // the MI + // Result record + if (!vCmd.Acknowledge()) { + // Report command acknowledge functionality failed + const SMICmdData cmdData(vCmd.GetCmdData()); + CmdStdout(cmdData); + CmdCauseAppExit(vCmd); + CmdDelete(cmdData.id); + + // Proceed to wait or execute next command + return MIstatus::success; + } + + // Retrieve the command's latest data/information. Needed for commands of the + // event type so have + // a record of commands pending finishing execution. + const CMIUtilString &rMIResultRecord(vCmd.GetMIResultRecord()); + SMICmdData cmdData( + vCmd.GetCmdData()); // Make a copy as the command will be deleted soon + cmdData.strMiCmdResultRecord = rMIResultRecord; // Precautionary copy as the + // command might forget to do + // this + if (vCmd.HasMIResultRecordExtra()) { + cmdData.bHasResultRecordExtra = true; + const CMIUtilString &rMIExtra(vCmd.GetMIResultRecordExtra()); + cmdData.strMiCmdResultRecordExtra = + rMIExtra; // Precautionary copy as the command might forget to do this + } + + // Send command's MI response to the client + bool bOk = CmdStdout(cmdData); + + // Delete the command object as do not require anymore + bOk = bOk && CmdDelete(vCmd.GetCmdData().id); + + return bOk; +} + +//++ +// Details: If the MI Driver is not operating via a client i.e. Eclipse check +// the command +// on failure suggests the application exits. A command can be such +// that a +// failure cannot the allow the application to continue operating. +// Args: vCmd - (R) Command object. +// Return: None. +// Return: None. +// Throws: None. +//-- +void CMICmdInvoker::CmdCauseAppExit(const CMICmdBase &vCmd) const { + if (vCmd.GetExitAppOnCommandFailure()) { + CMIDriver &rDriver(CMIDriver::Instance()); + if (rDriver.IsDriverDebuggingArgExecutable()) { + rDriver.SetExitApplicationFlag(true); + } + } +} + +//++ +// Details: Write to stdout and the Log file the command's MI formatted result. +// Type: vCmdData - (R) A command's information. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Return: None. +// Throws: None. +//-- +bool CMICmdInvoker::CmdStdout(const SMICmdData &vCmdData) const { + bool bOk = m_pLog->WriteLog(vCmdData.strMiCmdAll); + const bool bLock = bOk && m_rStreamOut.Lock(); + bOk = bOk && bLock && + m_rStreamOut.WriteMIResponse(vCmdData.strMiCmdResultRecord); + if (bOk && vCmdData.bHasResultRecordExtra) { + bOk = m_rStreamOut.WriteMIResponse(vCmdData.strMiCmdResultRecordExtra); + } + bOk = bLock && m_rStreamOut.Unlock(); + + return bOk; +} + +//++ +// Details: Required by the CMICmdMgr::ICmdDeleteCallback. *this object is +// registered +// with the Command Manager to receive callbacks when a command is +// being deleted. +// An object, *this invoker, does not delete a command object itself +// but calls +// the Command Manager to delete a command object. This function is the +// Invoker's +// called. +// The Invoker owns the command objects and so can delete them but must +// do it +// via the manager so other objects can be notified of the deletion. +// Type: Method. +// Args: vCmd - (RW) Command. +// Return: None. +// Throws: None. +//-- +void CMICmdInvoker::Delete(SMICmdData &vCmd) { CmdDelete(vCmd.id, true); } diff --git a/src/MICmdInvoker.h b/src/MICmdInvoker.h new file mode 100644 index 00000000000..5db8d36ad26 --- /dev/null +++ b/src/MICmdInvoker.h @@ -0,0 +1,103 @@ +//===-- MICmdInvoker.h ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// Third party headers +#include + +// In-house headers: +#include "MICmdData.h" +#include "MICmdMgrSetCmdDeleteCallback.h" +#include "MICmnBase.h" +#include "MIUtilSingletonBase.h" + +// Declarations: +class CMICmdBase; +class CMICmnStreamStdout; + +//++ +//============================================================================ +// Details: MI Command Invoker. The Invoker works on the command pattern design. +// There two main jobs; action command Execute() function, followed by +// the command's Acknowledge() function. When a command has finished +// its +// execute function it returns to the invoker. The invoker then calls +// the +// command's Acknowledge() function to do more work, form and give +// back a MI result. In the meantime the Command Monitor is monitoring +// the each command doing their Execute() function work so they do not +// exceed a time limit which if it exceeds informs the command(s) to +// stop work. +// The work by the Invoker is carried out in the main thread. +// The Invoker takes ownership of any commands created which means it +// is the only object to delete them when a command is finished +// working. +// A singleton class. +//-- +class CMICmdInvoker : public CMICmnBase, + public CMICmdMgrSetCmdDeleteCallback::ICallback, + public MI::ISingleton { + friend class MI::ISingleton; + + // Class: +public: + //++ + // Description: Invoker's interface for commands to implement. + //-- + class ICmd { + public: + virtual bool Acknowledge() = 0; + virtual bool Execute() = 0; + virtual bool ParseArgs() = 0; + virtual void SetCmdData(const SMICmdData &vCmdData) = 0; + virtual const SMICmdData &GetCmdData() const = 0; + virtual const CMIUtilString &GetErrorDescription() const = 0; + virtual void CmdFinishedTellInvoker() const = 0; + virtual const CMIUtilString &GetMIResultRecord() const = 0; + virtual const CMIUtilString &GetMIResultRecordExtra() const = 0; + virtual bool HasMIResultRecordExtra() const = 0; + + /* dtor */ virtual ~ICmd() {} + }; + + // Methods: +public: + bool Initialize() override; + bool Shutdown() override; + bool CmdExecute(CMICmdBase &vCmd); + bool CmdExecuteFinished(CMICmdBase &vCmd); + + // Typedefs: +private: + typedef std::map MapCmdIdToCmd_t; + typedef std::pair MapPairCmdIdToCmd_t; + + // Methods: +private: + /* ctor */ CMICmdInvoker(); + /* ctor */ CMICmdInvoker(const CMICmdInvoker &); + void operator=(const CMICmdInvoker &); + void CmdDeleteAll(); + bool CmdDelete(const MIuint vCmdId, const bool vbYesDeleteCmd = false); + bool CmdAdd(const CMICmdBase &vCmd); + bool CmdStdout(const SMICmdData &vCmdData) const; + void CmdCauseAppExit(const CMICmdBase &vCmd) const; + + // Overridden: +private: + // From CMICmnBase + /* dtor */ ~CMICmdInvoker() override; + // From CMICmdMgrSetCmdDeleteCallback::ICallback + void Delete(SMICmdData &vCmd) override; + + // Attributes: +private: + MapCmdIdToCmd_t m_mapCmdIdToCmd; + CMICmnStreamStdout &m_rStreamOut; +}; diff --git a/src/MICmdMgr.cpp b/src/MICmdMgr.cpp new file mode 100644 index 00000000000..453ffedbd33 --- /dev/null +++ b/src/MICmdMgr.cpp @@ -0,0 +1,248 @@ +//===-- MICmdMgr.cpp --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmdMgr.h" +#include "MICmdBase.h" +#include "MICmdFactory.h" +#include "MICmdInterpreter.h" +#include "MICmdInvoker.h" +#include "MICmnLog.h" +#include "MICmnResources.h" +#include "MIUtilSingletonBase.h" +#include "MIUtilSingletonHelper.h" + +//++ +// Details: CMICmdMgr constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdMgr::CMICmdMgr() + : m_interpretor(CMICmdInterpreter::Instance()), + m_factory(CMICmdFactory::Instance()), + m_invoker(CMICmdInvoker::Instance()) {} + +//++ +// Details: CMICmdMgr destructor. +// Type: Overridable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmdMgr::~CMICmdMgr() { Shutdown(); } + +//++ +// Details: Initialize resources for *this Command Manager. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmdMgr::Initialize() { + m_clientUsageRefCnt++; + + if (m_bInitialized) + return MIstatus::success; + + bool bOk = MIstatus::success; + CMIUtilString errMsg; + + // Note initialization order is important here as some resources depend on + // previous + MI::ModuleInit(IDS_MI_INIT_ERR_LOG, bOk, errMsg); + MI::ModuleInit(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg); + if (bOk && !m_interpretor.Initialize()) { + bOk = false; + errMsg = CMIUtilString::Format(MIRSRC(IDS_MI_INIT_ERR_CMDINTERPRETER), + m_interpretor.GetErrorDescription().c_str()); + } + if (bOk && !m_factory.Initialize()) { + bOk = false; + errMsg = CMIUtilString::Format(MIRSRC(IDS_MI_INIT_ERR_CMDFACTORY), + m_factory.GetErrorDescription().c_str()); + } + if (bOk && !m_invoker.Initialize()) { + bOk = false; + errMsg = CMIUtilString::Format(MIRSRC(IDS_MI_INIT_ERR_CMDINVOKER), + m_invoker.GetErrorDescription().c_str()); + } + m_bInitialized = bOk; + + if (!bOk) { + CMIUtilString strInitError( + CMIUtilString::Format(MIRSRC(IDS_MI_INIT_ERR_CMDMGR), errMsg.c_str())); + SetErrorDescription(strInitError); + return MIstatus::failure; + } + + return MIstatus::success; +} + +//++ +// Details: Release resources for *this Command Manager. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmdMgr::Shutdown() { + if (--m_clientUsageRefCnt > 0) + return MIstatus::success; + + if (!m_bInitialized) + return MIstatus::success; + + m_bInitialized = false; + + ClrErrorDescription(); + + bool bOk = MIstatus::success; + CMIUtilString errMsg; + + // Tidy up + m_setCmdDeleteCallback.clear(); + + // Note shutdown order is important here + if (!m_invoker.Shutdown()) { + bOk = false; + errMsg += CMIUtilString::Format(MIRSRC(IDS_MI_SHTDWN_ERR_CMDINVOKER), + m_invoker.GetErrorDescription().c_str()); + } + if (!m_factory.Shutdown()) { + bOk = false; + if (!errMsg.empty()) + errMsg += ", "; + errMsg += CMIUtilString::Format(MIRSRC(IDS_MI_SHTDWN_ERR_CMDFACTORY), + m_factory.GetErrorDescription().c_str()); + } + if (!m_interpretor.Shutdown()) { + bOk = false; + if (!errMsg.empty()) + errMsg += ", "; + errMsg += + CMIUtilString::Format(MIRSRC(IDS_MI_SHTDWN_ERR_CMDINTERPRETER), + m_interpretor.GetErrorDescription().c_str()); + } + MI::ModuleShutdown(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg); + MI::ModuleShutdown(IDS_MI_INIT_ERR_LOG, bOk, errMsg); + + if (!bOk) { + SetErrorDescriptionn(MIRSRC(IDS_MI_SHUTDOWN_ERR), errMsg.c_str()); + } + + return MIstatus::success; +} + +//++ +// Details: Establish whether the text data is an MI format type command. +// Type: Method. +// Args: vTextLine - (R) Text data to interpret. +// vwbYesValid - (W) True = MI type command, false = not +// recognised. +// vwbCmdNotInCmdFactor - (W) True = MI command not found in the +// command factor, false = recognised. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmdMgr::CmdInterpret(const CMIUtilString &vTextLine, bool &vwbYesValid, + bool &vwbCmdNotInCmdFactor, + SMICmdData &rwCmdData) { + return m_interpretor.ValidateIsMi(vTextLine, vwbYesValid, + vwbCmdNotInCmdFactor, rwCmdData); +} + +//++ +// Details: Having previously had the potential command validated and found +// valid now +// get the command executed. +// If the Functionality returns MIstatus::failure call +// GetErrorDescription(). +// This function is used by the application's main thread. +// Type: Method. +// Args: vCmdData - (RW) Command meta data. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmdMgr::CmdExecute(const SMICmdData &vCmdData) { + bool bOk = MIstatus::success; + + // Pass the command's meta data structure to the command + // so it can update it if required. (Need to copy it out of the + // command before the command is deleted) + CMICmdBase *pCmd = nullptr; + bOk = m_factory.CmdCreate(vCmdData.strMiCmd, vCmdData, pCmd); + if (!bOk) { + const CMIUtilString errMsg( + CMIUtilString::Format(MIRSRC(IDS_CMDMGR_ERR_CMD_FAILED_CREATE), + m_factory.GetErrorDescription().c_str())); + SetErrorDescription(errMsg); + return MIstatus::failure; + } + + bOk = m_invoker.CmdExecute(*pCmd); + if (!bOk) { + const CMIUtilString errMsg( + CMIUtilString::Format(MIRSRC(IDS_CMDMGR_ERR_CMD_INVOKER), + m_invoker.GetErrorDescription().c_str())); + SetErrorDescription(errMsg); + return MIstatus::failure; + } + + return bOk; +} + +//++ +// Details: Iterate all interested clients and tell them a command is being +// deleted. +// Type: Method. +// Args: vCmdData - (RW) The command to be deleted. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdMgr::CmdDelete(SMICmdData vCmdData) { + // Note vCmdData is a copy! The command holding its copy will be deleted soon + // we still need to iterate callback clients after a command object is deleted + + m_setCmdDeleteCallback.Delete(vCmdData); + + return MIstatus::success; +} + +//++ +// Details: Register an object to be called when a command object is deleted. +// Type: Method. +// Args: vObject - (R) A new interested client. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdMgr::CmdRegisterForDeleteNotification( + CMICmdMgrSetCmdDeleteCallback::ICallback &vObject) { + return m_setCmdDeleteCallback.Register(vObject); +} + +//++ +// Details: Unregister an object from being called when a command object is +// deleted. +// Type: Method. +// Args: vObject - (R) The was interested client. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmdMgr::CmdUnregisterForDeleteNotification( + CMICmdMgrSetCmdDeleteCallback::ICallback &vObject) { + return m_setCmdDeleteCallback.Unregister(vObject); +} diff --git a/src/MICmdMgr.h b/src/MICmdMgr.h new file mode 100644 index 00000000000..307ef897c58 --- /dev/null +++ b/src/MICmdMgr.h @@ -0,0 +1,69 @@ +//===-- MICmdMgr.h ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// Third party headers +#include + +// In-house headers: +#include "MICmdBase.h" +#include "MICmdMgrSetCmdDeleteCallback.h" +#include "MICmnBase.h" +#include "MIUtilSingletonBase.h" + +// Declarations: +class CMICmdInterpreter; +class CMICmdFactory; +class CMICmdInvoker; +class CMICmdBase; + +//++ +//============================================================================ +// Details: MI command manager. Oversees command operations, controls command +// production and the running of commands. +// Command Invoker, Command Factory and Command Monitor while +// independent +// units are overseen/managed by *this manager. +// A singleton class. +//-- +class CMICmdMgr : public CMICmnBase, public MI::ISingleton { + friend class MI::ISingleton; + + // Methods: +public: + bool Initialize() override; + bool Shutdown() override; + + bool CmdInterpret(const CMIUtilString &vTextLine, bool &vwbYesValid, + bool &vwbCmdNotInCmdFactor, SMICmdData &rwCmdData); + bool CmdExecute(const SMICmdData &vCmdData); + bool CmdDelete(SMICmdData vCmdData); + bool CmdRegisterForDeleteNotification( + CMICmdMgrSetCmdDeleteCallback::ICallback &vObject); + bool CmdUnregisterForDeleteNotification( + CMICmdMgrSetCmdDeleteCallback::ICallback &vObject); + + // Methods: +private: + /* ctor */ CMICmdMgr(); + /* ctor */ CMICmdMgr(const CMICmdMgr &); + void operator=(const CMICmdMgr &); + + // Overridden: +public: + // From CMICmnBase + /* dtor */ ~CMICmdMgr() override; + + // Attributes: +private: + CMICmdInterpreter &m_interpretor; + CMICmdFactory &m_factory; + CMICmdInvoker &m_invoker; + CMICmdMgrSetCmdDeleteCallback::CSetClients m_setCmdDeleteCallback; +}; diff --git a/src/MICmdMgrSetCmdDeleteCallback.cpp b/src/MICmdMgrSetCmdDeleteCallback.cpp new file mode 100644 index 00000000000..d6146b8d276 --- /dev/null +++ b/src/MICmdMgrSetCmdDeleteCallback.cpp @@ -0,0 +1,87 @@ +//===-- MICmdMgrSetCmdDeleteCallback.cpp ------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmdMgrSetCmdDeleteCallback.h" + +namespace CMICmdMgrSetCmdDeleteCallback { + +//++ +// Details: CSetClients constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CSetClients::CSetClients() : m_bClientUnregistered(false) {} + +//++ +// Details: CSetClients destructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CSetClients::~CSetClients() {} + +//++ +// Details: Register an object to be called when a command object is deleted. +// Type: Method. +// Args: vObject - (R) A new interested client. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CSetClients::Register(ICallback &vObject) { + insert(&vObject); + + return MIstatus::success; +} + +//++ +// Details: Unregister an object from being called when a command object is +// deleted. +// Type: Method. +// Args: vObject - (R) The was interested client. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CSetClients::Unregister(ICallback &vObject) { + m_bClientUnregistered = true; + erase(&vObject); + + return MIstatus::success; +} + +//++ +// Details: Iterate all interested clients and tell them a command is being +// deleted. +// Type: Method. +// Args: vCmd - (RW) The command to be deleted. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +void CSetClients::Delete(SMICmdData &vCmd) { + m_bClientUnregistered = false; // Reset + iterator it = begin(); + while (it != end()) { + ICallback *pObj = *it; + pObj->Delete(vCmd); + + if (m_bClientUnregistered) { + m_bClientUnregistered = false; // Reset + it = begin(); + } else + // Next + ++it; + } +} + +} // namespace CMICmdMgrSetCmdDeleteCallback diff --git a/src/MICmdMgrSetCmdDeleteCallback.h b/src/MICmdMgrSetCmdDeleteCallback.h new file mode 100644 index 00000000000..30df5e9cd42 --- /dev/null +++ b/src/MICmdMgrSetCmdDeleteCallback.h @@ -0,0 +1,72 @@ +//===-- MICmdMgrSetCmdDeleteCallback.h --------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +//-- + +#pragma once + +// Third party headers: +#include + +// In-house headers: +#include "MICmnBase.h" + +// Declarations: +struct SMICmdData; + +namespace CMICmdMgrSetCmdDeleteCallback { + +//++ +//============================================================================ +// Details: MI Command Manager interface for client call back. +// Objects that want to be notified of a command being deleted +// inherit this interface and register interest in command object +// deletion. An object deleting a command must not do it itself but +// call +// the Command Manager CmdDelete() function to delete a command object. +//-- +class ICallback { +public: + virtual void Delete(SMICmdData &vCmd) = 0; + + /* dtor */ virtual ~ICallback() {} +}; + +//++ +//============================================================================ +// Details: MI Command Manager container for clients registered interest in +// command +// objects being deleted. Objects register an interest so when a +// command +// is to be deleted that object wanting the delete calls the Command +// Manager to delete the command object. In so do all other registered +// objects get called to about the deletion including the object +// wanting +// to do the delete in the first place. +//-- +class CSetClients : public std::set, public CMICmnBase { + // Methods: +public: + /* ctor */ CSetClients(); + + bool Register(class ICallback &vObject); + bool Unregister(class ICallback &vObject); + void Delete(SMICmdData &vCmdData); + + // Overridden: +public: + // From CMICmnBase + /* dtor */ ~CSetClients() override; + + // Attributes: +private: + bool m_bClientUnregistered; // True = yes while deleting a client + // unregistered, false = no client unregistered + // during deletion +}; + +} // namespace CMICmdMgrSetCmdDeleteCallback diff --git a/src/MICmnBase.cpp b/src/MICmnBase.cpp new file mode 100644 index 00000000000..9d87064aa64 --- /dev/null +++ b/src/MICmnBase.cpp @@ -0,0 +1,123 @@ +//===-- MICmnBase.cpp -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Third party headers +#include + +// In-house headers: +#include "MICmnBase.h" +#include "MICmnLog.h" +#include "MICmnStreamStderr.h" + +//++ +// Details: CMICmnBase constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnBase::CMICmnBase() + : m_strMILastErrorDescription(CMIUtilString()), m_bInitialized(false), + m_pLog(&CMICmnLog::Instance()), m_clientUsageRefCnt(0) {} + +//++ +// Details: CMICmnBase destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnBase::~CMICmnBase() { m_pLog = nullptr; } + +//++ +// Details: Retrieve whether *this object has an error description set. +// Type: Method. +// Args: None. +// Return: bool - True = Yes already defined, false = empty description. +// Throws: None. +//-- +bool CMICmnBase::HaveErrorDescription() const { + return m_strMILastErrorDescription.empty(); +} + +//++ +// Details: Retrieve MI's last error condition. +// Type: Method. +// Args: None. +// Return: CMIUtilString & - Text description. +// Throws: None. +//-- +const CMIUtilString &CMICmnBase::GetErrorDescription() const { + return m_strMILastErrorDescription; +} + +//++ +// Details: Set MI's error condition description. This may be accessed by +// clients and +// seen by users. Message is available to the client using the server +// and sent +// to the Logger. +// Type: Method. +// Args: vrTxt - (R) Text description. +// Return: None. +// Throws: None. +//-- +void CMICmnBase::SetErrorDescription(const CMIUtilString &vrTxt) const { + m_strMILastErrorDescription = vrTxt; + if (!vrTxt.empty()) { + const CMIUtilString txt(CMIUtilString::Format("Error: %s", vrTxt.c_str())); + CMICmnStreamStderr::Instance().Write(txt); + } +} + +//++ +// Details: Set MI's error condition description. This may be accessed by +// clients and +// seen by users. Message is available to the client using the server +// and sent +// to the Logger. +// Type: Method. +// Args: vrTxt - (R) Text description. +// Return: None. +// Throws: None. +//-- +void CMICmnBase::SetErrorDescriptionNoLog(const CMIUtilString &vrTxt) const { + m_strMILastErrorDescription = vrTxt; +} + +//++ +// Details: Clear MI's error condition description. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMICmnBase::ClrErrorDescription() const { + m_strMILastErrorDescription.clear(); +} + +//++ +// Details: Set MI's error condition description. This may be accessed by +// clients and +// seen by users. Message is available to the client using the server +// and sent +// to the Logger. +// Type: Method. +// Args: vFormat - (R) Format string. +// ... - (R) Variable number of CMIUtilString type objects. +// Return: None. +// Throws: None. +//-- +void CMICmnBase::SetErrorDescriptionn(const char *vFormat, ...) const { + va_list args; + va_start(args, vFormat); + CMIUtilString strResult = CMIUtilString::FormatValist(vFormat, args); + va_end(args); + + SetErrorDescription(strResult); +} diff --git a/src/MICmnBase.h b/src/MICmnBase.h new file mode 100644 index 00000000000..368e912b6d8 --- /dev/null +++ b/src/MICmnBase.h @@ -0,0 +1,46 @@ +//===-- MICmnBase.h ---------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MIDataTypes.h" +#include "MIUtilString.h" + +// Declarations: +class CMICmnLog; + +//++ +//============================================================================ +// Details: MI common code implementation base class. +//-- +class CMICmnBase { + // Methods: +public: + /* ctor */ CMICmnBase(); + + bool HaveErrorDescription() const; + const CMIUtilString &GetErrorDescription() const; + void SetErrorDescription(const CMIUtilString &vrTxt) const; + void SetErrorDescriptionn(const char *vFormat, ...) const; + void SetErrorDescriptionNoLog(const CMIUtilString &vrTxt) const; + void ClrErrorDescription() const; + + // Overrideable: +public: + /* dtor */ virtual ~CMICmnBase(); + + // Attributes: +protected: + mutable CMIUtilString m_strMILastErrorDescription; + bool m_bInitialized; // True = yes successfully initialized, false = no yet or + // failed + CMICmnLog *m_pLog; // Allow all derived classes to use the logger + MIint m_clientUsageRefCnt; // Count of client using *this object so not + // shutdown() object to early +}; diff --git a/src/MICmnConfig.h b/src/MICmnConfig.h new file mode 100644 index 00000000000..9dd57922cec --- /dev/null +++ b/src/MICmnConfig.h @@ -0,0 +1,19 @@ +//===-- MICmnConfig.h -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +//-- +#pragma once + +// 1 = Show debug process attach modal dialog, 0 = do not show +// For windows only ATM, other OS's code is an infinite loop which a debugger +// must change a value to continue +#define MICONFIG_DEBUG_SHOW_ATTACH_DBG_DLG 0 + +// 1 = Write to MI's Log file warnings about commands that did not handle +// arguments or +// options present to them by the driver's client, 0 = no warnings given +#define MICONFIG_GIVE_WARNING_CMD_ARGS_NOT_HANDLED 1 diff --git a/src/MICmnLLDBBroadcaster.cpp b/src/MICmnLLDBBroadcaster.cpp new file mode 100644 index 00000000000..554ec1f7e9f --- /dev/null +++ b/src/MICmnLLDBBroadcaster.cpp @@ -0,0 +1,68 @@ +//===-- MICmnLLDBBroadcaster.cpp --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmnLLDBBroadcaster.h" + +//++ +// Details: CMICmnLLDBBroadcaster constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnLLDBBroadcaster::CMICmnLLDBBroadcaster() + : lldb::SBBroadcaster("MI driver") {} + +//++ +// Details: CMICmnLLDBBroadcaster destructor. +// Type: Overridable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnLLDBBroadcaster::~CMICmnLLDBBroadcaster() { Shutdown(); } + +//++ +// Details: Initialize resources for *this broadcaster object. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBBroadcaster::Initialize() { + m_clientUsageRefCnt++; + + if (m_bInitialized) + return MIstatus::success; + + m_bInitialized = MIstatus::success; + + return m_bInitialized; +} + +//++ +// Details: Release resources for *this broadcaster object. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBBroadcaster::Shutdown() { + if (--m_clientUsageRefCnt > 0) + return MIstatus::success; + + if (!m_bInitialized) + return MIstatus::success; + + m_bInitialized = false; + + return MIstatus::success; +} diff --git a/src/MICmnLLDBBroadcaster.h b/src/MICmnLLDBBroadcaster.h new file mode 100644 index 00000000000..326b6769fcb --- /dev/null +++ b/src/MICmnLLDBBroadcaster.h @@ -0,0 +1,44 @@ +//===-- MICmnLLDBBroadcaster.h ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmnBase.h" +#include "MIUtilSingletonBase.h" +#include "lldb/API/SBBroadcaster.h" + +//++ +//============================================================================ +// Details: MI derived class from LLDB SBBroadcaster API. +// +// *** This class (files) is a place holder until we know we need it or +// *** not +// +// A singleton class. +//-- +class CMICmnLLDBBroadcaster : public CMICmnBase, + public lldb::SBBroadcaster, + public MI::ISingleton { + friend MI::ISingleton; + + // Methods: +public: + bool Initialize() override; + bool Shutdown() override; + // Methods: +private: + /* ctor */ CMICmnLLDBBroadcaster(); + /* ctor */ CMICmnLLDBBroadcaster(const CMICmnLLDBBroadcaster &); + void operator=(const CMICmnLLDBBroadcaster &); + + // Overridden: +private: + // From CMICmnBase + /* dtor */ ~CMICmnLLDBBroadcaster() override; +}; diff --git a/src/MICmnLLDBDebugSessionInfo.cpp b/src/MICmnLLDBDebugSessionInfo.cpp new file mode 100644 index 00000000000..5ec2c588e20 --- /dev/null +++ b/src/MICmnLLDBDebugSessionInfo.cpp @@ -0,0 +1,863 @@ +//===-- MICmnLLDBDebugSessionInfo.cpp ---------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Third party headers: +#include "lldb/API/SBThread.h" +#include +#ifdef _WIN32 +#include +#else +#include +#endif // _WIN32 +#include "lldb/API/SBBreakpointLocation.h" + +// In-house headers: +#include "MICmdData.h" +#include "MICmnLLDBDebugSessionInfo.h" +#include "MICmnLLDBDebugger.h" +#include "MICmnLLDBUtilSBValue.h" +#include "MICmnMIResultRecord.h" +#include "MICmnMIValueConst.h" +#include "MICmnMIValueList.h" +#include "MICmnMIValueTuple.h" +#include "MICmnResources.h" +#include "Platform.h" + +//++ +// Details: CMICmnLLDBDebugSessionInfo constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnLLDBDebugSessionInfo::CMICmnLLDBDebugSessionInfo() + : m_nBrkPointCntMax(INT32_MAX), + m_currentSelectedThread(LLDB_INVALID_THREAD_ID), + m_constStrSharedDataKeyWkDir("Working Directory"), + m_constStrSharedDataSolibPath("Solib Path"), + m_constStrPrintCharArrayAsString("Print CharArrayAsString"), + m_constStrPrintExpandAggregates("Print ExpandAggregates"), + m_constStrPrintAggregateFieldNames("Print AggregateFieldNames") {} + +//++ +// Details: CMICmnLLDBDebugSessionInfo destructor. +// Type: Overridable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnLLDBDebugSessionInfo::~CMICmnLLDBDebugSessionInfo() { Shutdown(); } + +//++ +// Details: Initialize resources for *this object. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugSessionInfo::Initialize() { + m_clientUsageRefCnt++; + + if (m_bInitialized) + return MIstatus::success; + + m_currentSelectedThread = LLDB_INVALID_THREAD_ID; + CMICmnLLDBDebugSessionInfoVarObj::VarObjIdResetToZero(); + + m_bInitialized = MIstatus::success; + + return m_bInitialized; +} + +//++ +// Details: Release resources for *this object. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugSessionInfo::Shutdown() { + if (--m_clientUsageRefCnt > 0) + return MIstatus::success; + + if (!m_bInitialized) + return MIstatus::success; + + // Tidy up + SharedDataDestroy(); + + m_vecActiveThreadId.clear(); + CMICmnLLDBDebugSessionInfoVarObj::VarObjClear(); + + m_bInitialized = false; + + return MIstatus::success; +} + +//++ +// Details: Command instances can create and share data between other instances +// of commands. +// Data can also be assigned by a command and retrieved by LLDB event +// handler. +// This function takes down those resources build up over the use of +// the commands. +// This function should be called when the creation and running of +// command has +// stopped i.e. application shutdown. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMICmnLLDBDebugSessionInfo::SharedDataDestroy() { + m_mapIdToSessionData.Clear(); + m_vecVarObj.clear(); + m_mapBrkPtIdToBrkPtInfo.clear(); +} + +//++ +// Details: Record information about a LLDB break point so that is can be +// recalled in other +// commands or LLDB event handling functions. +// Type: Method. +// Args: vBrkPtId - (R) LLDB break point ID. +// vrBrkPtInfo - (R) Break point information object. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugSessionInfo::RecordBrkPtInfo( + const MIuint vnBrkPtId, const SBrkPtInfo &vrBrkPtInfo) { + MapPairBrkPtIdToBrkPtInfo_t pr(vnBrkPtId, vrBrkPtInfo); + m_mapBrkPtIdToBrkPtInfo.insert(pr); + + return MIstatus::success; +} + +//++ +// Details: Retrieve information about a LLDB break point previous recorded +// either by +// commands or LLDB event handling functions. +// Type: Method. +// Args: vBrkPtId - (R) LLDB break point ID. +// vrwBrkPtInfo - (W) Break point information object. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugSessionInfo::RecordBrkPtInfoGet( + const MIuint vnBrkPtId, SBrkPtInfo &vrwBrkPtInfo) const { + const MapBrkPtIdToBrkPtInfo_t::const_iterator it = + m_mapBrkPtIdToBrkPtInfo.find(vnBrkPtId); + if (it != m_mapBrkPtIdToBrkPtInfo.end()) { + vrwBrkPtInfo = (*it).second; + return MIstatus::success; + } + + return MIstatus::failure; +} + +//++ +// Details: Delete information about a specific LLDB break point object. This +// function +// should be called when a LLDB break point is deleted. +// Type: Method. +// Args: vBrkPtId - (R) LLDB break point ID. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugSessionInfo::RecordBrkPtInfoDelete(const MIuint vnBrkPtId) { + const MapBrkPtIdToBrkPtInfo_t::const_iterator it = + m_mapBrkPtIdToBrkPtInfo.find(vnBrkPtId); + if (it != m_mapBrkPtIdToBrkPtInfo.end()) { + m_mapBrkPtIdToBrkPtInfo.erase(it); + return MIstatus::success; + } + + return MIstatus::failure; +} + +//++ +// Details: Retrieve the specified thread's frame information. +// Type: Method. +// Args: vCmdData - (R) A command's information. +// vThreadIdx - (R) Thread index. +// vwrThreadFrames - (W) Frame data. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugSessionInfo::GetThreadFrames( + const SMICmdData &vCmdData, const MIuint vThreadIdx, + const FrameInfoFormat_e veFrameInfoFormat, CMIUtilString &vwrThreadFrames) { + lldb::SBThread thread = GetProcess().GetThreadByIndexID(vThreadIdx); + const uint32_t nFrames = thread.GetNumFrames(); + if (nFrames == 0) { + // MI print "frame={}" + CMICmnMIValueTuple miValueTuple; + CMICmnMIValueResult miValueResult("frame", miValueTuple); + vwrThreadFrames = miValueResult.GetString(); + return MIstatus::success; + } + + // MI print + // "frame={level=\"%d\",addr=\"0x%016" PRIx64 + // "\",func=\"%s\",args=[%s],file=\"%s\",fullname=\"%s\",line=\"%d\"},frame={level=\"%d\",addr=\"0x%016" + // PRIx64 "\",func=\"%s\",args=[%s],file=\"%s\",fullname=\"%s\",line=\"%d\"}, + // ..." + CMIUtilString strListCommaSeparated; + for (MIuint nLevel = 0; nLevel < nFrames; nLevel++) { + CMICmnMIValueTuple miValueTuple; + if (!MIResponseFormFrameInfo(thread, nLevel, veFrameInfoFormat, + miValueTuple)) + return MIstatus::failure; + + const CMICmnMIValueResult miValueResult2("frame", miValueTuple); + if (nLevel != 0) + strListCommaSeparated += ","; + strListCommaSeparated += miValueResult2.GetString(); + } + + vwrThreadFrames = strListCommaSeparated; + + return MIstatus::success; +} + +//++ +// Details: Return the resolved file's path for the given file. +// Type: Method. +// Args: vCmdData - (R) A command's information. +// vPath - (R) Original path. +// vwrResolvedPath - (W) Resolved path. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugSessionInfo::ResolvePath(const SMICmdData &vCmdData, + const CMIUtilString &vPath, + CMIUtilString &vwrResolvedPath) { + // ToDo: Verify this code as it does not work as vPath is always empty + + CMIUtilString strResolvedPath; + if (!SharedDataRetrieve(m_constStrSharedDataKeyWkDir, + strResolvedPath)) { + vwrResolvedPath = ""; + SetErrorDescription(CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_SHARED_DATA_NOT_FOUND), vCmdData.strMiCmd.c_str(), + m_constStrSharedDataKeyWkDir.c_str())); + return MIstatus::failure; + } + + vwrResolvedPath = vPath; + + return ResolvePath(strResolvedPath, vwrResolvedPath); +} + +//++ +// Details: Return the resolved file's path for the given file. +// Type: Method. +// Args: vstrUnknown - (R) String assigned to path when resolved path +// is empty. +// vwrResolvedPath - (RW) The original path overwritten with resolved +// path. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugSessionInfo::ResolvePath(const CMIUtilString &vstrUnknown, + CMIUtilString &vwrResolvedPath) { + if (vwrResolvedPath.size() < 1) { + vwrResolvedPath = vstrUnknown; + return MIstatus::success; + } + + bool bOk = MIstatus::success; + + CMIUtilString::VecString_t vecPathFolders; + const MIuint nSplits = vwrResolvedPath.Split("/", vecPathFolders); + MIunused(nSplits); + MIuint nFoldersBack = 1; // 1 is just the file (last element of vector) + while (bOk && (vecPathFolders.size() >= nFoldersBack)) { + CMIUtilString strTestPath; + MIuint nFoldersToAdd = nFoldersBack; + while (nFoldersToAdd > 0) { + strTestPath += "/"; + strTestPath += vecPathFolders[vecPathFolders.size() - nFoldersToAdd]; + nFoldersToAdd--; + } + bool bYesAccessible = false; + bOk = AccessPath(strTestPath, bYesAccessible); + if (bYesAccessible) { + vwrResolvedPath = strTestPath; + return MIstatus::success; + } else + nFoldersBack++; + } + + // No files exist in the union of working directory and debuginfo path + // Simply use the debuginfo path and let the IDE handle it. + + return bOk; +} + +//++ +// Details: Determine the given file path exists or not. +// Type: Method. +// Args: vPath - (R) File name path. +// vwbYesAccessible - (W) True - file exists, false = does not +// exist. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugSessionInfo::AccessPath(const CMIUtilString &vPath, + bool &vwbYesAccessible) { +#ifdef _WIN32 + vwbYesAccessible = (::_access(vPath.c_str(), 0) == 0); +#else + vwbYesAccessible = (::access(vPath.c_str(), 0) == 0); +#endif // _WIN32 + + return MIstatus::success; +} + +//++ +// Details: Form MI partial response by appending more MI value type objects to +// the +// tuple type object past in. +// Type: Method. +// Args: vCmdData - (R) A command's information. +// vrThread - (R) LLDB thread object. +// vwrMIValueTuple - (W) MI value tuple object. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugSessionInfo::MIResponseFormThreadInfo( + const SMICmdData &vCmdData, const lldb::SBThread &vrThread, + const ThreadInfoFormat_e veThreadInfoFormat, + CMICmnMIValueTuple &vwrMIValueTuple) { + lldb::SBThread &rThread = const_cast(vrThread); + + const bool bSuspended = rThread.IsSuspended(); + const lldb::StopReason eReason = rThread.GetStopReason(); + const bool bValidReason = !((eReason == lldb::eStopReasonNone) || + (eReason == lldb::eStopReasonInvalid)); + const CMIUtilString strState((bSuspended || bValidReason) ? "stopped" + : "running"); + + // Add "id" + const CMIUtilString strId(CMIUtilString::Format("%d", rThread.GetIndexID())); + const CMICmnMIValueConst miValueConst1(strId); + const CMICmnMIValueResult miValueResult1("id", miValueConst1); + vwrMIValueTuple.Add(miValueResult1); + + // Add "target-id" + const char *pThreadName = rThread.GetName(); + const MIuint len = CMIUtilString(pThreadName).length(); + const bool bHaveName = (len > 0) && (len < 32) && // 32 is arbitrary number + CMIUtilString::IsAllValidAlphaAndNumeric(pThreadName); + const char *pThrdFmt = bHaveName ? "%s" : "Thread %d"; + CMIUtilString strThread; + if (bHaveName) + strThread = CMIUtilString::Format(pThrdFmt, pThreadName); + else + strThread = CMIUtilString::Format(pThrdFmt, rThread.GetIndexID()); + const CMICmnMIValueConst miValueConst2(strThread); + const CMICmnMIValueResult miValueResult2("target-id", miValueConst2); + vwrMIValueTuple.Add(miValueResult2); + + // Add "frame" + if (veThreadInfoFormat != eThreadInfoFormat_NoFrames) { + CMIUtilString strFrames; + if (!GetThreadFrames(vCmdData, rThread.GetIndexID(), + eFrameInfoFormat_AllArgumentsInSimpleForm, strFrames)) + return MIstatus::failure; + + const CMICmnMIValueConst miValueConst3(strFrames, true); + vwrMIValueTuple.Add(miValueConst3, false); + } + + // Add "state" + const CMICmnMIValueConst miValueConst4(strState); + const CMICmnMIValueResult miValueResult4("state", miValueConst4); + vwrMIValueTuple.Add(miValueResult4); + + return MIstatus::success; +} + +//++ +// Details: Form MI partial response by appending more MI value type objects to +// the +// tuple type object past in. +// Type: Method. +// Args: vrFrame - (R) LLDB thread object. +// vMaskVarTypes - (R) Construed according to VariableType_e. +// veVarInfoFormat - (R) The type of variable info that should be +// shown. +// vwrMIValueList - (W) MI value list object. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugSessionInfo::MIResponseFormVariableInfo( + const lldb::SBFrame &vrFrame, const MIuint vMaskVarTypes, + const VariableInfoFormat_e veVarInfoFormat, + CMICmnMIValueList &vwrMiValueList, const MIuint vnMaxDepth, /* = 10 */ + const bool vbMarkArgs /* = false*/) { + bool bOk = MIstatus::success; + lldb::SBFrame &rFrame = const_cast(vrFrame); + + const bool bArg = (vMaskVarTypes & eVariableType_Arguments); + const bool bLocals = (vMaskVarTypes & eVariableType_Locals); + const bool bStatics = (vMaskVarTypes & eVariableType_Statics); + const bool bInScopeOnly = (vMaskVarTypes & eVariableType_InScope); + + // Handle arguments first + lldb::SBValueList listArg = rFrame.GetVariables(bArg, false, false, false); + bOk = bOk && MIResponseForVariableInfoInternal(veVarInfoFormat, + vwrMiValueList, listArg, + vnMaxDepth, true, vbMarkArgs); + + // Handle remaining variables + lldb::SBValueList listVars = + rFrame.GetVariables(false, bLocals, bStatics, bInScopeOnly); + bOk = bOk && MIResponseForVariableInfoInternal(veVarInfoFormat, + vwrMiValueList, listVars, + vnMaxDepth, false, vbMarkArgs); + + return bOk; +} + +bool CMICmnLLDBDebugSessionInfo::MIResponseForVariableInfoInternal( + const VariableInfoFormat_e veVarInfoFormat, + CMICmnMIValueList &vwrMiValueList, const lldb::SBValueList &vwrSBValueList, + const MIuint vnMaxDepth, const bool vbIsArgs, const bool vbMarkArgs) { + const MIuint nArgs = vwrSBValueList.GetSize(); + for (MIuint i = 0; i < nArgs; i++) { + CMICmnMIValueTuple miValueTuple; + lldb::SBValue value = vwrSBValueList.GetValueAtIndex(i); + // If one stops inside try block with, which catch clause type is unnamed + // (e.g std::exception&) then value name will be nullptr as well as value + // pointer + const char *name = value.GetName(); + if (name == nullptr) + continue; + const CMICmnMIValueConst miValueConst(name); + const CMICmnMIValueResult miValueResultName("name", miValueConst); + if (vbMarkArgs && vbIsArgs) { + const CMICmnMIValueConst miValueConstArg("1"); + const CMICmnMIValueResult miValueResultArg("arg", miValueConstArg); + miValueTuple.Add(miValueResultArg); + } + if (veVarInfoFormat != eVariableInfoFormat_NoValues) { + miValueTuple.Add(miValueResultName); // name + if (veVarInfoFormat == eVariableInfoFormat_SimpleValues) { + const CMICmnMIValueConst miValueConst3(value.GetTypeName()); + const CMICmnMIValueResult miValueResult3("type", miValueConst3); + miValueTuple.Add(miValueResult3); + } + const MIuint nChildren = value.GetNumChildren(); + const bool bIsPointerType = value.GetType().IsPointerType(); + if (nChildren == 0 || // no children + (bIsPointerType && nChildren == 1) || // pointers + veVarInfoFormat == eVariableInfoFormat_AllValues) // show all values + { + CMIUtilString strValue; + if (GetVariableInfo(value, vnMaxDepth == 0, strValue)) { + const CMICmnMIValueConst miValueConst2( + strValue.Escape().AddSlashes()); + const CMICmnMIValueResult miValueResult2("value", miValueConst2); + miValueTuple.Add(miValueResult2); + } + } + vwrMiValueList.Add(miValueTuple); + continue; + } + + if (vbMarkArgs) { + // If we are printing names only with vbMarkArgs, we still need to add the + // name to the value tuple + miValueTuple.Add(miValueResultName); // name + vwrMiValueList.Add(miValueTuple); + } else { + // If we are printing name only then no need to put it in the tuple. + vwrMiValueList.Add(miValueResultName); + } + } + return MIstatus::success; +} + +//++ +// Details: Extract the value's name and value or recurse into child value +// object. +// Type: Method. +// Args: vrValue - (R) LLDB value object. +// vbInSimpleForm - (R) True = Get variable info in simple form (i.e. +// don't expand aggregates). +// - False = Get variable info (and expand +// aggregates if any). +// vwrStrValue t - (W) The string representation of this value. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugSessionInfo::GetVariableInfo(const lldb::SBValue &vrValue, + const bool vbInSimpleForm, + CMIUtilString &vwrStrValue) { + const CMICmnLLDBUtilSBValue utilValue(vrValue, true, false); + const bool bExpandAggregates = !vbInSimpleForm; + vwrStrValue = utilValue.GetValue(bExpandAggregates); + return MIstatus::success; +} + +//++ +// Details: Form MI partial response by appending more MI value type objects to +// the +// tuple type object past in. +// Type: Method. +// Args: vrThread - (R) LLDB thread object. +// vwrMIValueTuple - (W) MI value tuple object. +// vArgInfo - (R) Args information in MI response form. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugSessionInfo::MIResponseFormFrameInfo( + const lldb::SBThread &vrThread, const MIuint vnLevel, + const FrameInfoFormat_e veFrameInfoFormat, + CMICmnMIValueTuple &vwrMiValueTuple) { + lldb::SBThread &rThread = const_cast(vrThread); + + lldb::SBFrame frame = rThread.GetFrameAtIndex(vnLevel); + lldb::addr_t pc = 0; + CMIUtilString fnName; + CMIUtilString fileName; + CMIUtilString path; + MIuint nLine = 0; + if (!GetFrameInfo(frame, pc, fnName, fileName, path, nLine)) + return MIstatus::failure; + + // MI print "{level=\"0\",addr=\"0x%016" PRIx64 + // "\",func=\"%s\",file=\"%s\",fullname=\"%s\",line=\"%d\"}" + const CMIUtilString strLevel(CMIUtilString::Format("%d", vnLevel)); + const CMICmnMIValueConst miValueConst(strLevel); + const CMICmnMIValueResult miValueResult("level", miValueConst); + vwrMiValueTuple.Add(miValueResult); + const CMIUtilString strAddr(CMIUtilString::Format("0x%016" PRIx64, pc)); + const CMICmnMIValueConst miValueConst2(strAddr); + const CMICmnMIValueResult miValueResult2("addr", miValueConst2); + vwrMiValueTuple.Add(miValueResult2); + const CMICmnMIValueConst miValueConst3(fnName); + const CMICmnMIValueResult miValueResult3("func", miValueConst3); + vwrMiValueTuple.Add(miValueResult3); + if (veFrameInfoFormat != eFrameInfoFormat_NoArguments) { + CMICmnMIValueList miValueList(true); + const MIuint maskVarTypes = eVariableType_Arguments; + if (veFrameInfoFormat == eFrameInfoFormat_AllArgumentsInSimpleForm) { + if (!MIResponseFormVariableInfo(frame, maskVarTypes, + eVariableInfoFormat_AllValues, + miValueList, 0)) + return MIstatus::failure; + } else if (!MIResponseFormVariableInfo(frame, maskVarTypes, + eVariableInfoFormat_AllValues, + miValueList)) + return MIstatus::failure; + + const CMICmnMIValueResult miValueResult4("args", miValueList); + vwrMiValueTuple.Add(miValueResult4); + } + const CMICmnMIValueConst miValueConst5(fileName); + const CMICmnMIValueResult miValueResult5("file", miValueConst5); + vwrMiValueTuple.Add(miValueResult5); + const CMICmnMIValueConst miValueConst6(path); + const CMICmnMIValueResult miValueResult6("fullname", miValueConst6); + vwrMiValueTuple.Add(miValueResult6); + const CMIUtilString strLine(CMIUtilString::Format("%d", nLine)); + const CMICmnMIValueConst miValueConst7(strLine); + const CMICmnMIValueResult miValueResult7("line", miValueConst7); + vwrMiValueTuple.Add(miValueResult7); + + return MIstatus::success; +} + +//++ +// Details: Retrieve the frame information from LLDB frame object. +// Type: Method. +// Args: vrFrame - (R) LLDB thread object. +// vPc - (W) Address number. +// vFnName - (W) Function name. +// vFileName - (W) File name text. +// vPath - (W) Full file name and path text. +// vnLine - (W) File line number. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugSessionInfo::GetFrameInfo( + const lldb::SBFrame &vrFrame, lldb::addr_t &vwPc, CMIUtilString &vwFnName, + CMIUtilString &vwFileName, CMIUtilString &vwPath, MIuint &vwnLine) { + lldb::SBFrame &rFrame = const_cast(vrFrame); + + static char pBuffer[PATH_MAX]; + const MIuint nBytes = + rFrame.GetLineEntry().GetFileSpec().GetPath(&pBuffer[0], sizeof(pBuffer)); + MIunused(nBytes); + CMIUtilString strResolvedPath(&pBuffer[0]); + const char *pUnkwn = "??"; + if (!ResolvePath(pUnkwn, strResolvedPath)) + return MIstatus::failure; + vwPath = strResolvedPath; + + vwPc = rFrame.GetPC(); + + const char *pFnName = rFrame.GetFunctionName(); + vwFnName = (pFnName != nullptr) ? pFnName : pUnkwn; + + const char *pFileName = rFrame.GetLineEntry().GetFileSpec().GetFilename(); + vwFileName = (pFileName != nullptr) ? pFileName : pUnkwn; + + vwnLine = rFrame.GetLineEntry().GetLine(); + + return MIstatus::success; +} + +//++ +// Details: Form MI partial response by appending more MI value type objects to +// the +// tuple type object past in. +// Type: Method. +// Args: vrBrkPtInfo - (R) Break point information object. +// vwrMIValueTuple - (W) MI value tuple object. +// Return: None. +// Throws: None. +//-- +void CMICmnLLDBDebugSessionInfo::MIResponseFormBrkPtFrameInfo( + const SBrkPtInfo &vrBrkPtInfo, CMICmnMIValueTuple &vwrMiValueTuple) { + const CMIUtilString strAddr( + CMIUtilString::Format("0x%016" PRIx64, vrBrkPtInfo.m_pc)); + const CMICmnMIValueConst miValueConst2(strAddr); + const CMICmnMIValueResult miValueResult2("addr", miValueConst2); + vwrMiValueTuple.Add(miValueResult2); + const CMICmnMIValueConst miValueConst3(vrBrkPtInfo.m_fnName); + const CMICmnMIValueResult miValueResult3("func", miValueConst3); + vwrMiValueTuple.Add(miValueResult3); + const CMICmnMIValueConst miValueConst5(vrBrkPtInfo.m_fileName); + const CMICmnMIValueResult miValueResult5("file", miValueConst5); + vwrMiValueTuple.Add(miValueResult5); + const CMIUtilString strN5 = CMIUtilString::Format( + "%s/%s", vrBrkPtInfo.m_path.c_str(), vrBrkPtInfo.m_fileName.c_str()); + const CMICmnMIValueConst miValueConst6(strN5); + const CMICmnMIValueResult miValueResult6("fullname", miValueConst6); + vwrMiValueTuple.Add(miValueResult6); + const CMIUtilString strLine(CMIUtilString::Format("%d", vrBrkPtInfo.m_nLine)); + const CMICmnMIValueConst miValueConst7(strLine); + const CMICmnMIValueResult miValueResult7("line", miValueConst7); + vwrMiValueTuple.Add(miValueResult7); +} + +//++ +// Details: Form MI partial response by appending more MI value type objects to +// the +// tuple type object past in. +// Type: Method. +// Args: vrBrkPtInfo - (R) Break point information object. +// vwrMIValueTuple - (W) MI value tuple object. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugSessionInfo::MIResponseFormBrkPtInfo( + const SBrkPtInfo &vrBrkPtInfo, CMICmnMIValueTuple &vwrMiValueTuple) { + // MI print + // "=breakpoint-modified,bkpt={number=\"%d\",type=\"breakpoint\",disp=\"%s\",enabled=\"%c\",addr=\"0x%016" + // PRIx64 "\", + // func=\"%s\",file=\"%s\",fullname=\"%s/%s\",line=\"%d\",times=\"%d\",original-location=\"%s\"}" + + // "number=" + const CMICmnMIValueConst miValueConst( + CMIUtilString::Format("%d", vrBrkPtInfo.m_id)); + const CMICmnMIValueResult miValueResult("number", miValueConst); + CMICmnMIValueTuple miValueTuple(miValueResult); + // "type=" + const CMICmnMIValueConst miValueConst2(vrBrkPtInfo.m_strType); + const CMICmnMIValueResult miValueResult2("type", miValueConst2); + miValueTuple.Add(miValueResult2); + // "disp=" + const CMICmnMIValueConst miValueConst3(vrBrkPtInfo.m_bDisp ? "del" : "keep"); + const CMICmnMIValueResult miValueResult3("disp", miValueConst3); + miValueTuple.Add(miValueResult3); + // "enabled=" + const CMICmnMIValueConst miValueConst4(vrBrkPtInfo.m_bEnabled ? "y" : "n"); + const CMICmnMIValueResult miValueResult4("enabled", miValueConst4); + miValueTuple.Add(miValueResult4); + // "addr=" + // "func=" + // "file=" + // "fullname=" + // "line=" + MIResponseFormBrkPtFrameInfo(vrBrkPtInfo, miValueTuple); + // "pending=" + if (vrBrkPtInfo.m_bPending) { + const CMICmnMIValueConst miValueConst(vrBrkPtInfo.m_strOrigLoc); + const CMICmnMIValueList miValueList(miValueConst); + const CMICmnMIValueResult miValueResult("pending", miValueList); + miValueTuple.Add(miValueResult); + } + if (vrBrkPtInfo.m_bHaveArgOptionThreadGrp) { + const CMICmnMIValueConst miValueConst(vrBrkPtInfo.m_strOptThrdGrp); + const CMICmnMIValueList miValueList(miValueConst); + const CMICmnMIValueResult miValueResult("thread-groups", miValueList); + miValueTuple.Add(miValueResult); + } + // "times=" + const CMICmnMIValueConst miValueConstB( + CMIUtilString::Format("%d", vrBrkPtInfo.m_nTimes)); + const CMICmnMIValueResult miValueResultB("times", miValueConstB); + miValueTuple.Add(miValueResultB); + // "thread=" + if (vrBrkPtInfo.m_bBrkPtThreadId) { + const CMICmnMIValueConst miValueConst( + CMIUtilString::Format("%d", vrBrkPtInfo.m_nBrkPtThreadId)); + const CMICmnMIValueResult miValueResult("thread", miValueConst); + miValueTuple.Add(miValueResult); + } + // "cond=" + if (vrBrkPtInfo.m_bCondition) { + const CMICmnMIValueConst miValueConst(vrBrkPtInfo.m_strCondition); + const CMICmnMIValueResult miValueResult("cond", miValueConst); + miValueTuple.Add(miValueResult); + } + // "ignore=" + if (vrBrkPtInfo.m_nIgnore != 0) { + const CMICmnMIValueConst miValueConst( + CMIUtilString::Format("%d", vrBrkPtInfo.m_nIgnore)); + const CMICmnMIValueResult miValueResult("ignore", miValueConst); + miValueTuple.Add(miValueResult); + } + // "original-location=" + const CMICmnMIValueConst miValueConstC(vrBrkPtInfo.m_strOrigLoc); + const CMICmnMIValueResult miValueResultC("original-location", miValueConstC); + miValueTuple.Add(miValueResultC); + + vwrMiValueTuple = miValueTuple; + + return MIstatus::success; +} + +//++ +// Details: Retrieve breakpoint information and write into the given breakpoint +// information +// object. Note not all possible information is retrieved and so the +// information +// object may need to be filled in with more information after calling +// this +// function. Mainly breakpoint location information of information that +// is +// unlikely to change. +// Type: Method. +// Args: vBrkPt - (R) LLDB break point object. +// vrBrkPtInfo - (W) Break point information object. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugSessionInfo::GetBrkPtInfo(const lldb::SBBreakpoint &vBrkPt, + SBrkPtInfo &vrwBrkPtInfo) const { + lldb::SBBreakpoint &rBrkPt = const_cast(vBrkPt); + lldb::SBBreakpointLocation brkPtLoc = rBrkPt.GetLocationAtIndex(0); + lldb::SBAddress brkPtAddr = brkPtLoc.GetAddress(); + lldb::SBSymbolContext symbolCntxt = + brkPtAddr.GetSymbolContext(lldb::eSymbolContextEverything); + const char *pUnkwn = "??"; + lldb::SBModule rModule = symbolCntxt.GetModule(); + const char *pModule = + rModule.IsValid() ? rModule.GetFileSpec().GetFilename() : pUnkwn; + MIunused(pModule); + const char *pFile = pUnkwn; + const char *pFn = pUnkwn; + const char *pFilePath = pUnkwn; + size_t nLine = 0; + lldb::addr_t nAddr = brkPtAddr.GetLoadAddress(GetTarget()); + if (nAddr == LLDB_INVALID_ADDRESS) + nAddr = brkPtAddr.GetFileAddress(); + + lldb::SBCompileUnit rCmplUnit = symbolCntxt.GetCompileUnit(); + if (rCmplUnit.IsValid()) { + lldb::SBFileSpec rFileSpec = rCmplUnit.GetFileSpec(); + pFile = rFileSpec.GetFilename(); + pFilePath = rFileSpec.GetDirectory(); + lldb::SBFunction rFn = symbolCntxt.GetFunction(); + if (rFn.IsValid()) + pFn = rFn.GetName(); + lldb::SBLineEntry rLnEntry = symbolCntxt.GetLineEntry(); + if (rLnEntry.GetLine() > 0) + nLine = rLnEntry.GetLine(); + } + + vrwBrkPtInfo.m_id = vBrkPt.GetID(); + vrwBrkPtInfo.m_strType = "breakpoint"; + vrwBrkPtInfo.m_pc = nAddr; + vrwBrkPtInfo.m_fnName = pFn; + vrwBrkPtInfo.m_fileName = pFile; + vrwBrkPtInfo.m_path = pFilePath; + vrwBrkPtInfo.m_nLine = nLine; + vrwBrkPtInfo.m_nTimes = vBrkPt.GetHitCount(); + + return MIstatus::success; +} + +//++ +// Details: Get current debugger. +// Type: Method. +// Args: None. +// Return: lldb::SBDebugger - current debugger. +// Throws: None. +//-- +lldb::SBDebugger &CMICmnLLDBDebugSessionInfo::GetDebugger() const { + return CMICmnLLDBDebugger::Instance().GetTheDebugger(); +} + +//++ +// Details: Get current listener. +// Type: Method. +// Args: None. +// Return: lldb::SBListener - current listener. +// Throws: None. +//-- +lldb::SBListener &CMICmnLLDBDebugSessionInfo::GetListener() const { + return CMICmnLLDBDebugger::Instance().GetTheListener(); +} + +//++ +// Details: Get current target. +// Type: Method. +// Args: None. +// Return: lldb::SBTarget - current target. +// Throws: None. +//-- +lldb::SBTarget CMICmnLLDBDebugSessionInfo::GetTarget() const { + auto target = GetDebugger().GetSelectedTarget(); + if (target.IsValid()) + return target; + return GetDebugger().GetDummyTarget(); +} + +//++ +// Details: Get current process. +// Type: Method. +// Args: None. +// Return: lldb::SBProcess - current process. +// Throws: None. +//-- +lldb::SBProcess CMICmnLLDBDebugSessionInfo::GetProcess() const { + return GetTarget().GetProcess(); +} diff --git a/src/MICmnLLDBDebugSessionInfo.h b/src/MICmnLLDBDebugSessionInfo.h new file mode 100644 index 00000000000..1fa81951f43 --- /dev/null +++ b/src/MICmnLLDBDebugSessionInfo.h @@ -0,0 +1,292 @@ +//===-- MICmnLLDBDebugSessionInfo.h -----------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// Third party headers: +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBListener.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBTarget.h" +#include +#include + +// In-house headers: +#include "MICmnBase.h" +#include "MICmnLLDBDebugSessionInfoVarObj.h" +#include "MICmnMIValueTuple.h" +#include "MIUtilMapIdToVariant.h" +#include "MIUtilSingletonBase.h" +#include "MIUtilThreadBaseStd.h" + +// Declarations: +class CMICmnLLDBDebugger; +struct SMICmdData; +class CMICmnMIValueTuple; +class CMICmnMIValueList; + +//++ +//============================================================================ +// Details: MI debug session object that holds debugging information between +// instances of MI commands executing their work and producing MI +// result records. Information/data is set by one or many commands then +// retrieved by the same or other subsequent commands. +// It primarily holds LLDB type objects. +// A singleton class. +//-- +class CMICmnLLDBDebugSessionInfo + : public CMICmnBase, + public MI::ISingleton { + friend class MI::ISingleton; + + // Structs: +public: + //++ + //============================================================================ + // Details: Break point information object. Used to easily pass information + // about + // a break around and record break point information to be recalled + // by + // other commands or LLDB event handling functions. + //-- + struct SBrkPtInfo { + SBrkPtInfo() + : m_id(0), m_bDisp(false), m_bEnabled(false), m_pc(0), m_nLine(0), + m_bHaveArgOptionThreadGrp(false), m_nTimes(0), m_bPending(false), + m_nIgnore(0), m_bCondition(false), m_bBrkPtThreadId(false), + m_nBrkPtThreadId(0) {} + + MIuint m_id; // LLDB break point ID. + CMIUtilString m_strType; // Break point type. + bool m_bDisp; // True = "del", false = "keep". + bool m_bEnabled; // True = enabled, false = disabled break point. + lldb::addr_t m_pc; // Address number. + CMIUtilString m_fnName; // Function name. + CMIUtilString m_fileName; // File name text. + CMIUtilString m_path; // Full file name and path text. + MIuint m_nLine; // File line number. + bool m_bHaveArgOptionThreadGrp; // True = include MI field, false = do not + // include "thread-groups". + CMIUtilString m_strOptThrdGrp; // Thread group number. + MIuint m_nTimes; // The count of the breakpoint existence. + CMIUtilString m_strOrigLoc; // The name of the break point. + bool m_bPending; // True = the breakpoint has not been established yet, + // false = location found + MIuint m_nIgnore; // The number of time the breakpoint is run over before it + // is stopped on a hit + bool m_bCondition; // True = break point is conditional, use condition + // expression, false = no condition + CMIUtilString m_strCondition; // Break point condition expression + bool m_bBrkPtThreadId; // True = break point is specified to work with a + // specific thread, false = no specified thread given + MIuint + m_nBrkPtThreadId; // Restrict the breakpoint to the specified thread-id + }; + + // Enumerations: +public: + //++ =================================================================== + // Details: The type of variable used by MIResponseFormVariableInfo family + // functions. + //-- + enum VariableType_e { + eVariableType_InScope = (1u << 0), // In scope only. + eVariableType_Statics = (1u << 1), // Statics. + eVariableType_Locals = (1u << 2), // Locals. + eVariableType_Arguments = (1u << 3) // Arguments. + }; + + //++ =================================================================== + // Details: Determine the information that should be shown by using + // MIResponseFormVariableInfo family functions. + //-- + enum VariableInfoFormat_e { + eVariableInfoFormat_NoValues = 0, + eVariableInfoFormat_AllValues = 1, + eVariableInfoFormat_SimpleValues = 2 + }; + + //++ =================================================================== + // Details: Determine the information that should be shown by using + // MIResponseFormThreadInfo family functions. + //-- + enum ThreadInfoFormat_e { + eThreadInfoFormat_NoFrames, + eThreadInfoFormat_AllFrames + }; + + //++ =================================================================== + // Details: Determine the information that should be shown by using + // MIResponseFormFrameInfo family functions. + //-- + enum FrameInfoFormat_e { + eFrameInfoFormat_NoArguments, + eFrameInfoFormat_AllArguments, + eFrameInfoFormat_AllArgumentsInSimpleForm + }; + + // Typedefs: +public: + typedef std::vector VecActiveThreadId_t; + + // Methods: +public: + bool Initialize() override; + bool Shutdown() override; + + // Variant type data which can be assigned and retrieved across all command + // instances + template + bool SharedDataAdd(const CMIUtilString &vKey, const T &vData); + template + bool SharedDataRetrieve(const CMIUtilString &vKey, T &vwData); + void SharedDataDestroy(); + + // Common command required functionality + bool AccessPath(const CMIUtilString &vPath, bool &vwbYesAccessible); + bool ResolvePath(const SMICmdData &vCmdData, const CMIUtilString &vPath, + CMIUtilString &vwrResolvedPath); + bool ResolvePath(const CMIUtilString &vstrUnknown, + CMIUtilString &vwrResolvedPath); + bool MIResponseFormFrameInfo(const lldb::SBThread &vrThread, + const MIuint vnLevel, + const FrameInfoFormat_e veFrameInfoFormat, + CMICmnMIValueTuple &vwrMiValueTuple); + bool MIResponseFormThreadInfo(const SMICmdData &vCmdData, + const lldb::SBThread &vrThread, + const ThreadInfoFormat_e veThreadInfoFormat, + CMICmnMIValueTuple &vwrMIValueTuple); + bool MIResponseFormVariableInfo(const lldb::SBFrame &vrFrame, + const MIuint vMaskVarTypes, + const VariableInfoFormat_e veVarInfoFormat, + CMICmnMIValueList &vwrMiValueList, + const MIuint vnMaxDepth = 10, + const bool vbMarkArgs = false); + void MIResponseFormBrkPtFrameInfo(const SBrkPtInfo &vrBrkPtInfo, + CMICmnMIValueTuple &vwrMiValueTuple); + bool MIResponseFormBrkPtInfo(const SBrkPtInfo &vrBrkPtInfo, + CMICmnMIValueTuple &vwrMiValueTuple); + bool GetBrkPtInfo(const lldb::SBBreakpoint &vBrkPt, + SBrkPtInfo &vrwBrkPtInfo) const; + bool RecordBrkPtInfo(const MIuint vnBrkPtId, const SBrkPtInfo &vrBrkPtInfo); + bool RecordBrkPtInfoGet(const MIuint vnBrkPtId, + SBrkPtInfo &vrwBrkPtInfo) const; + bool RecordBrkPtInfoDelete(const MIuint vnBrkPtId); + CMIUtilThreadMutex &GetSessionMutex() { return m_sessionMutex; } + lldb::SBDebugger &GetDebugger() const; + lldb::SBListener &GetListener() const; + lldb::SBTarget GetTarget() const; + lldb::SBProcess GetProcess() const; + + // Attributes: +public: + // The following are available to all command instances + const MIuint m_nBrkPointCntMax; + VecActiveThreadId_t m_vecActiveThreadId; + lldb::tid_t m_currentSelectedThread; + + // These are keys that can be used to access the shared data map + // Note: This list is expected to grow and will be moved and abstracted in the + // future. + const CMIUtilString m_constStrSharedDataKeyWkDir; + const CMIUtilString m_constStrSharedDataSolibPath; + const CMIUtilString m_constStrPrintCharArrayAsString; + const CMIUtilString m_constStrPrintExpandAggregates; + const CMIUtilString m_constStrPrintAggregateFieldNames; + + // Typedefs: +private: + typedef std::vector VecVarObj_t; + typedef std::map MapBrkPtIdToBrkPtInfo_t; + typedef std::pair MapPairBrkPtIdToBrkPtInfo_t; + + // Methods: +private: + /* ctor */ CMICmnLLDBDebugSessionInfo(); + /* ctor */ CMICmnLLDBDebugSessionInfo(const CMICmnLLDBDebugSessionInfo &); + void operator=(const CMICmnLLDBDebugSessionInfo &); + // + bool GetVariableInfo(const lldb::SBValue &vrValue, const bool vbInSimpleForm, + CMIUtilString &vwrStrValue); + bool GetFrameInfo(const lldb::SBFrame &vrFrame, lldb::addr_t &vwPc, + CMIUtilString &vwFnName, CMIUtilString &vwFileName, + CMIUtilString &vwPath, MIuint &vwnLine); + bool GetThreadFrames(const SMICmdData &vCmdData, const MIuint vThreadIdx, + const FrameInfoFormat_e veFrameInfoFormat, + CMIUtilString &vwrThreadFrames); + bool + MIResponseForVariableInfoInternal(const VariableInfoFormat_e veVarInfoFormat, + CMICmnMIValueList &vwrMiValueList, + const lldb::SBValueList &vwrSBValueList, + const MIuint vnMaxDepth, + const bool vbIsArgs, const bool vbMarkArgs); + + // Overridden: +private: + // From CMICmnBase + /* dtor */ ~CMICmnLLDBDebugSessionInfo() override; + + // Attributes: +private: + CMIUtilMapIdToVariant m_mapIdToSessionData; // Hold and retrieve key to value + // data available across all + // commands + VecVarObj_t m_vecVarObj; // Vector of session variable objects + MapBrkPtIdToBrkPtInfo_t m_mapBrkPtIdToBrkPtInfo; + CMIUtilThreadMutex m_sessionMutex; +}; + +//++ +// Details: Command instances can create and share data between other instances +// of commands. +// This function adds new data to the shared data. Using the same ID +// more than +// once replaces any previous matching data keys. +// Type: Template method. +// Args: T - The type of the object to be stored. +// vKey - (R) A non empty unique data key to retrieve the data by. +// vData - (R) Data to be added to the share. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +template +bool CMICmnLLDBDebugSessionInfo::SharedDataAdd(const CMIUtilString &vKey, + const T &vData) { + if (!m_mapIdToSessionData.Add(vKey, vData)) { + SetErrorDescription(m_mapIdToSessionData.GetErrorDescription()); + return MIstatus::failure; + } + + return MIstatus::success; +} + +//++ +// Details: Command instances can create and share data between other instances +// of commands. +// This function retrieves data from the shared data container. +// Type: Method. +// Args: T - The type of the object being retrieved. +// vKey - (R) A non empty unique data key to retrieve the data by. +// vData - (W) The data. +// Return: bool - True = data found, false = data not found or an error +// occurred trying to fetch. +// Throws: None. +//-- +template +bool CMICmnLLDBDebugSessionInfo::SharedDataRetrieve(const CMIUtilString &vKey, + T &vwData) { + bool bDataFound = false; + + if (!m_mapIdToSessionData.Get(vKey, vwData, bDataFound)) { + SetErrorDescription(m_mapIdToSessionData.GetErrorDescription()); + return MIstatus::failure; + } + + return bDataFound; +} diff --git a/src/MICmnLLDBDebugSessionInfoVarObj.cpp b/src/MICmnLLDBDebugSessionInfoVarObj.cpp new file mode 100644 index 00000000000..fc0f76b6d49 --- /dev/null +++ b/src/MICmnLLDBDebugSessionInfoVarObj.cpp @@ -0,0 +1,573 @@ +//===-- MICmnLLDBDebugSessionInfoVarObj.cpp ---------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmnLLDBDebugSessionInfoVarObj.h" +#include "MICmnLLDBProxySBValue.h" +#include "MICmnLLDBUtilSBValue.h" + +// Instantiations: +const char *CMICmnLLDBDebugSessionInfoVarObj::ms_aVarFormatStrings[] = { + // CODETAG_SESSIONINFO_VARFORMAT_ENUM + // *** Order is import here. + "", "binary", "octal", "decimal", + "hexadecimal", "natural"}; +const char *CMICmnLLDBDebugSessionInfoVarObj::ms_aVarFormatChars[] = { + // CODETAG_SESSIONINFO_VARFORMAT_ENUM + // *** Order is import here. + "", "t", "o", "d", "x", "N"}; +CMICmnLLDBDebugSessionInfoVarObj::MapKeyToVarObj_t + CMICmnLLDBDebugSessionInfoVarObj::ms_mapVarIdToVarObj; +MIuint CMICmnLLDBDebugSessionInfoVarObj::ms_nVarUniqueId = 0; // Index from 0 +CMICmnLLDBDebugSessionInfoVarObj::varFormat_e + CMICmnLLDBDebugSessionInfoVarObj::ms_eDefaultFormat = eVarFormat_Natural; + +//++ +// Details: CMICmnLLDBDebugSessionInfoVarObj constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnLLDBDebugSessionInfoVarObj::CMICmnLLDBDebugSessionInfoVarObj() + : m_eVarFormat(eVarFormat_Natural), m_eVarType(eVarType_Internal) { + // Do not call UpdateValue() in here as not necessary +} + +//++ +// Details: CMICmnLLDBDebugSessionInfoVarObj constructor. +// Type: Method. +// Args: vrStrNameReal - (R) The actual name of the variable, the +// expression. +// vrStrName - (R) The name given for *this var object. +// vrValue - (R) The LLDB SBValue object represented by *this +// object. +// Return: None. +// Throws: None. +//-- +CMICmnLLDBDebugSessionInfoVarObj::CMICmnLLDBDebugSessionInfoVarObj( + const CMIUtilString &vrStrNameReal, const CMIUtilString &vrStrName, + const lldb::SBValue &vrValue) + : m_eVarFormat(eVarFormat_Natural), m_eVarType(eVarType_Internal), + m_strName(vrStrName), m_SBValue(vrValue), m_strNameReal(vrStrNameReal) { + UpdateValue(); +} + +//++ +// Details: CMICmnLLDBDebugSessionInfoVarObj constructor. +// Type: Method. +// Args: vrStrNameReal - (R) The actual name of the variable, the +// expression. +// vrStrName - (R) The name given for *this var object. +// vrValue - (R) The LLDB SBValue object represented by +// *this object. +// vrStrVarObjParentName - (R) The var object parent to *this var +// object (LLDB SBValue equivalent). +// Return: None. +// Throws: None. +//-- +CMICmnLLDBDebugSessionInfoVarObj::CMICmnLLDBDebugSessionInfoVarObj( + const CMIUtilString &vrStrNameReal, const CMIUtilString &vrStrName, + const lldb::SBValue &vrValue, const CMIUtilString &vrStrVarObjParentName) + : m_eVarFormat(eVarFormat_Natural), m_eVarType(eVarType_Internal), + m_strName(vrStrName), m_SBValue(vrValue), m_strNameReal(vrStrNameReal), + m_strVarObjParentName(vrStrVarObjParentName) { + UpdateValue(); +} + +//++ +// Details: CMICmnLLDBDebugSessionInfoVarObj copy constructor. +// Type: Method. +// Args: vrOther - (R) The object to copy from. +// Return: None. +// Throws: None. +//-- +CMICmnLLDBDebugSessionInfoVarObj::CMICmnLLDBDebugSessionInfoVarObj( + const CMICmnLLDBDebugSessionInfoVarObj &vrOther) { + CopyOther(vrOther); +} + +//++ +// Details: CMICmnLLDBDebugSessionInfoVarObj copy constructor. +// Type: Method. +// Args: vrOther - (R) The object to copy from. +// Return: None. +// Throws: None. +//-- +CMICmnLLDBDebugSessionInfoVarObj::CMICmnLLDBDebugSessionInfoVarObj( + CMICmnLLDBDebugSessionInfoVarObj &vrOther) { + CopyOther(vrOther); +} + +//++ +// Details: CMICmnLLDBDebugSessionInfoVarObj move constructor. +// Type: Method. +// Args: vrwOther - (R) The object to copy from. +// Return: None. +// Throws: None. +//-- +CMICmnLLDBDebugSessionInfoVarObj::CMICmnLLDBDebugSessionInfoVarObj( + CMICmnLLDBDebugSessionInfoVarObj &&vrwOther) { + MoveOther(vrwOther); +} + +//++ +// Details: CMICmnLLDBDebugSessionInfoVarObj assignment operator. +// Type: Method. +// Args: vrOther - (R) The object to copy from. +// Return: CMICmnLLDBDebugSessionInfoVarObj & - Updated *this object. +// Throws: None. +//-- +CMICmnLLDBDebugSessionInfoVarObj &CMICmnLLDBDebugSessionInfoVarObj:: +operator=(const CMICmnLLDBDebugSessionInfoVarObj &vrOther) { + CopyOther(vrOther); + + return *this; +} + +//++ +// Details: CMICmnLLDBDebugSessionInfoVarObj assignment operator. +// Type: Method. +// Args: vrwOther - (R) The object to copy from. +// Return: CMICmnLLDBDebugSessionInfoVarObj & - Updated *this object. +// Throws: None. +//-- +CMICmnLLDBDebugSessionInfoVarObj &CMICmnLLDBDebugSessionInfoVarObj:: +operator=(CMICmnLLDBDebugSessionInfoVarObj &&vrwOther) { + MoveOther(vrwOther); + + return *this; +} + +//++ +// Details: Copy the other instance of that object to *this object. +// Type: Method. +// Args: vrOther - (R) The object to copy from. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugSessionInfoVarObj::CopyOther( + const CMICmnLLDBDebugSessionInfoVarObj &vrOther) { + // Check for self-assignment + if (this == &vrOther) + return MIstatus::success; + + m_eVarFormat = vrOther.m_eVarFormat; + m_eVarType = vrOther.m_eVarType; + m_strName = vrOther.m_strName; + m_SBValue = vrOther.m_SBValue; + m_strNameReal = vrOther.m_strNameReal; + m_strFormattedValue = vrOther.m_strFormattedValue; + m_strVarObjParentName = vrOther.m_strVarObjParentName; + + return MIstatus::success; +} + +//++ +// Details: Move that object to *this object. +// Type: Method. +// Args: vrwOther - (RW) The object to copy from. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugSessionInfoVarObj::MoveOther( + CMICmnLLDBDebugSessionInfoVarObj &vrwOther) { + // Check for self-assignment + if (this == &vrwOther) + return MIstatus::success; + + CopyOther(vrwOther); + vrwOther.m_eVarFormat = eVarFormat_Natural; + vrwOther.m_eVarType = eVarType_Internal; + vrwOther.m_strName.clear(); + vrwOther.m_SBValue.Clear(); + vrwOther.m_strNameReal.clear(); + vrwOther.m_strFormattedValue.clear(); + vrwOther.m_strVarObjParentName.clear(); + + return MIstatus::success; +} + +//++ +// Details: CMICmnLLDBDebugSessionInfoVarObj destructor. +// Type: Overridden. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnLLDBDebugSessionInfoVarObj::~CMICmnLLDBDebugSessionInfoVarObj() {} + +//++ +// Details: Retrieve the var format enumeration for the specified string. +// Type: Static method. +// Args: vrStrFormat - (R) Text description of the var format. +// Return: varFormat_e - Var format enumeration. +// - No match found return eVarFormat_Invalid. +// Throws: None. +//-- +CMICmnLLDBDebugSessionInfoVarObj::varFormat_e +CMICmnLLDBDebugSessionInfoVarObj::GetVarFormatForString( + const CMIUtilString &vrStrFormat) { + // CODETAG_SESSIONINFO_VARFORMAT_ENUM + for (MIuint i = 0; i < eVarFormat_count; i++) { + const char *pVarFormatString = ms_aVarFormatStrings[i]; + if (vrStrFormat == pVarFormatString) + return static_cast(i); + } + + return eVarFormat_Invalid; +} + +//++ +// Details: Retrieve the var format enumeration for the specified character. +// Type: Static method. +// Args: vcFormat - Character representing the var format. +// Return: varFormat_e - Var format enumeration. +// - No match found return eVarFormat_Invalid. +// Throws: None. +//-- +CMICmnLLDBDebugSessionInfoVarObj::varFormat_e +CMICmnLLDBDebugSessionInfoVarObj::GetVarFormatForChar(char vcFormat) { + if ('r' == vcFormat) + return eVarFormat_Hex; + + // CODETAG_SESSIONINFO_VARFORMAT_ENUM + for (MIuint i = 0; i < eVarFormat_count; i++) { + const char *pVarFormatChar = ms_aVarFormatChars[i]; + if (*pVarFormatChar == vcFormat) + return static_cast(i); + } + + return eVarFormat_Invalid; +} + +//++ +// Details: Return the equivalent var value formatted string for the given value +// type, +// which was prepared for printing (i.e. value was escaped and now it's +// ready +// for wrapping into quotes). +// The SBValue vrValue parameter is checked by LLDB private code for +// valid +// scalar type via MI Driver proxy function as the valued returned can +// also be +// an error condition. The proxy function determines if the check was +// valid +// otherwise return an error condition state by other means saying so. +// Type: Static method. +// Args: vrValue - (R) The var value object. +// veVarFormat - (R) Var format enumeration. +// Returns: CMIUtilString - Value formatted string. +// Throws: None. +//-- +CMIUtilString CMICmnLLDBDebugSessionInfoVarObj::GetValueStringFormatted( + const lldb::SBValue &vrValue, + const CMICmnLLDBDebugSessionInfoVarObj::varFormat_e veVarFormat) { + const CMICmnLLDBUtilSBValue utilValue(vrValue, true); + if (utilValue.IsIntegerType()) { + MIuint64 nValue = 0; + if (CMICmnLLDBProxySBValue::GetValueAsUnsigned(vrValue, nValue)) { + lldb::SBValue &rValue = const_cast(vrValue); + return GetStringFormatted(nValue, rValue.GetValue(), veVarFormat); + } + } + + return utilValue.GetValue().AddSlashes(); +} + +//++ +// Details: Return number formatted string according to the given value type. +// Type: Static method. +// Args: vnValue - (R) The number value to get formatted. +// vpStrValueNatural - (R) The natural representation of the number +// value. +// veVarFormat - (R) Var format enumeration. +// Returns: CMIUtilString - Numerical formatted string. +// Throws: None. +//-- +CMIUtilString CMICmnLLDBDebugSessionInfoVarObj::GetStringFormatted( + const MIuint64 vnValue, const char *vpStrValueNatural, + const CMICmnLLDBDebugSessionInfoVarObj::varFormat_e veVarFormat) { + CMIUtilString strFormattedValue; + CMICmnLLDBDebugSessionInfoVarObj::varFormat_e veFormat = veVarFormat; + if (ms_eDefaultFormat != eVarFormat_Invalid && + veVarFormat == eVarFormat_Natural) { + veFormat = ms_eDefaultFormat; + } + + switch (veFormat) { + case eVarFormat_Binary: + strFormattedValue = CMIUtilString::FormatBinary(vnValue); + break; + case eVarFormat_Octal: + strFormattedValue = CMIUtilString::Format("0%llo", vnValue); + break; + case eVarFormat_Decimal: + strFormattedValue = CMIUtilString::Format("%lld", vnValue); + break; + case eVarFormat_Hex: + strFormattedValue = CMIUtilString::Format("0x%llx", vnValue); + break; + case eVarFormat_Natural: + default: { + strFormattedValue = (vpStrValueNatural != nullptr) ? vpStrValueNatural : ""; + } + } + + return strFormattedValue; +} + +//++ +// Details: Delete internal container contents. +// Type: Static method. +// Args: None. +// Returns: None. +// Throws: None. +//-- +void CMICmnLLDBDebugSessionInfoVarObj::VarObjClear() { + ms_mapVarIdToVarObj.clear(); +} + +//++ +// Details: Add a var object to the internal container. +// Type: Static method. +// Args: vrVarObj - (R) The var value object. +// Returns: None. +// Throws: None. +//-- +void CMICmnLLDBDebugSessionInfoVarObj::VarObjAdd( + const CMICmnLLDBDebugSessionInfoVarObj &vrVarObj) { + VarObjDelete(vrVarObj.GetName()); + MapPairKeyToVarObj_t pr(vrVarObj.GetName(), vrVarObj); + ms_mapVarIdToVarObj.insert(pr); +} + +//++ +// Details: Delete the var object from the internal container matching the +// specified name. +// Type: Static method. +// Args: vrVarName - (R) The var value name. +// Returns: None. +// Throws: None. +//-- +void CMICmnLLDBDebugSessionInfoVarObj::VarObjDelete( + const CMIUtilString &vrVarName) { + const MapKeyToVarObj_t::const_iterator it = + ms_mapVarIdToVarObj.find(vrVarName); + if (it != ms_mapVarIdToVarObj.end()) { + ms_mapVarIdToVarObj.erase(it); + } +} + +//++ +// Details: Update an existing var object in the internal container. +// Type: Static method. +// Args: vrVarObj - (R) The var value object. +// Returns: None. +// Throws: None. +//-- +void CMICmnLLDBDebugSessionInfoVarObj::VarObjUpdate( + const CMICmnLLDBDebugSessionInfoVarObj &vrVarObj) { + VarObjAdd(vrVarObj); +} + +//++ +// Details: Retrieve the var object matching the specified name. +// Type: Static method. +// Args: vrVarName - (R) The var value name. +// vrwVarObj - (W) A var object. +// Returns: bool - True = object found, false = object not found. +// Throws: None. +//-- +bool CMICmnLLDBDebugSessionInfoVarObj::VarObjGet( + const CMIUtilString &vrVarName, + CMICmnLLDBDebugSessionInfoVarObj &vrwVarObj) { + const MapKeyToVarObj_t::const_iterator it = + ms_mapVarIdToVarObj.find(vrVarName); + if (it != ms_mapVarIdToVarObj.end()) { + const CMICmnLLDBDebugSessionInfoVarObj &rVarObj = (*it).second; + vrwVarObj = rVarObj; + return true; + } + + return false; +} + +//++ +// Details: A count is kept of the number of var value objects created. This is +// count is +// used to ID the var value object. Reset the count to 0. +// Type: Static method. +// Args: None. +// Returns: None. +// Throws: None. +//-- +void CMICmnLLDBDebugSessionInfoVarObj::VarObjIdResetToZero() { + ms_nVarUniqueId = 0; +} + +//++ +// Details: Default format is globally used as the data format when "natural" is +// in effect, that is, this overrides the default +// Type: Static method. +// Args: None. +// Returns: None. +// Throws: None. +//-- +void CMICmnLLDBDebugSessionInfoVarObj::VarObjSetFormat( + varFormat_e eDefaultFormat) { + ms_eDefaultFormat = eDefaultFormat; +} + +//++ +// Details: A count is kept of the number of var value objects created. This is +// count is +// used to ID the var value object. Increment the count by 1. +// Type: Static method. +// Args: None. +// Returns: None. +// Throws: None. +//-- +void CMICmnLLDBDebugSessionInfoVarObj::VarObjIdInc() { ms_nVarUniqueId++; } + +//++ +// Details: A count is kept of the number of var value objects created. This is +// count is +// used to ID the var value object. Retrieve ID. +// Type: Static method. +// Args: None. +// Returns: None. +// Throws: None. +//-- +MIuint CMICmnLLDBDebugSessionInfoVarObj::VarObjIdGet() { + return ms_nVarUniqueId; +} + +//++ +// Details: Retrieve the value formatted object's name. +// Type: Method. +// Args: None. +// Returns: CMIUtilString & - Value's var%u name text. +// Throws: None. +//-- +const CMIUtilString &CMICmnLLDBDebugSessionInfoVarObj::GetName() const { + return m_strName; +} + +//++ +// Details: Retrieve the value formatted object's variable name as given in the +// MI command +// to create the var object. +// Type: Method. +// Args: None. +// Returns: CMIUtilString & - Value's real name text. +// Throws: None. +//-- +const CMIUtilString &CMICmnLLDBDebugSessionInfoVarObj::GetNameReal() const { + return m_strNameReal; +} + +//++ +// Details: Retrieve the value formatted string. +// Type: Method. +// Args: None. +// Returns: CMIUtilString & - Value formatted string. +// Throws: None. +//-- +const CMIUtilString & +CMICmnLLDBDebugSessionInfoVarObj::GetValueFormatted() const { + return m_strFormattedValue; +} + +//++ +// Details: Retrieve the LLDB Value object. +// Type: Method. +// Args: None. +// Returns: lldb::SBValue & - LLDB Value object. +// Throws: None. +//-- +lldb::SBValue &CMICmnLLDBDebugSessionInfoVarObj::GetValue() { + return m_SBValue; +} + +//++ +// Details: Retrieve the LLDB Value object. +// Type: Method. +// Args: None. +// Returns: lldb::SBValue & - Constant LLDB Value object. +// Throws: None. +//-- +const lldb::SBValue &CMICmnLLDBDebugSessionInfoVarObj::GetValue() const { + return m_SBValue; +} + +//++ +// Details: Set the var format type for *this object and update the formatting. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugSessionInfoVarObj::SetVarFormat( + const varFormat_e veVarFormat) { + if (veVarFormat >= eVarFormat_count) + return MIstatus::failure; + + m_eVarFormat = veVarFormat; + UpdateValue(); + return MIstatus::success; +} + +//++ +// Details: Update *this var obj. Update it's value and type. +// Type: Method. +// Args: None. +// Returns: None. +// Throws: None. +//-- +void CMICmnLLDBDebugSessionInfoVarObj::UpdateValue() { + m_strFormattedValue = GetValueStringFormatted(m_SBValue, m_eVarFormat); + + MIuint64 nValue = 0; + if (CMICmnLLDBProxySBValue::GetValueAsUnsigned(m_SBValue, nValue) == + MIstatus::failure) + m_eVarType = eVarType_Composite; + + CMICmnLLDBDebugSessionInfoVarObj::VarObjUpdate(*this); +} + +//++ +// Details: Retrieve the enumeration type of the var object. +// Type: Method. +// Args: None. +// Returns: varType_e - Enumeration value. +// Throws: None. +//-- +CMICmnLLDBDebugSessionInfoVarObj::varType_e +CMICmnLLDBDebugSessionInfoVarObj::GetType() const { + return m_eVarType; +} + +//++ +// Details: Retrieve the parent var object's name, the parent var object to +// *this var +// object (if assigned). The parent is equivalent to LLDB SBValue +// variable's +// parent. +// Type: Method. +// Args: None. +// Returns: CMIUtilString & - Pointer to var object, NULL = no parent. +// Throws: None. +//-- +const CMIUtilString & +CMICmnLLDBDebugSessionInfoVarObj::GetVarParentName() const { + return m_strVarObjParentName; +} diff --git a/src/MICmnLLDBDebugSessionInfoVarObj.h b/src/MICmnLLDBDebugSessionInfoVarObj.h new file mode 100644 index 00000000000..6cee598fccc --- /dev/null +++ b/src/MICmnLLDBDebugSessionInfoVarObj.h @@ -0,0 +1,139 @@ +//===-- MICmnLLDBDebugSessionInfoVarObj.h -----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// Third Party Headers: +#include "lldb/API/SBValue.h" +#include + +// In-house headers: +#include "MIUtilString.h" + +//++ +//============================================================================ +// Details: MI debug session variable object. The static functionality in *this +// class manages a map container of *these variable objects. +//-- +class CMICmnLLDBDebugSessionInfoVarObj { + // Enums: +public: + //++ ---------------------------------------------------------------------- + // Details: Enumeration of a variable type that is not a composite type + //-- + enum varFormat_e { + // CODETAG_SESSIONINFO_VARFORMAT_ENUM + // *** Order is import here *** + eVarFormat_Invalid = 0, + eVarFormat_Binary, + eVarFormat_Octal, + eVarFormat_Decimal, + eVarFormat_Hex, + eVarFormat_Natural, + eVarFormat_count // Always last one + }; + + //++ ---------------------------------------------------------------------- + // Details: Enumeration of a variable type by composite or internal type + //-- + enum varType_e { + eVarType_InValid = 0, + eVarType_Composite, // i.e. struct + eVarType_Internal, // i.e. int + eVarType_count // Always last one + }; + + // Statics: +public: + static varFormat_e GetVarFormatForString(const CMIUtilString &vrStrFormat); + static varFormat_e GetVarFormatForChar(char vcFormat); + static CMIUtilString GetValueStringFormatted(const lldb::SBValue &vrValue, + const varFormat_e veVarFormat); + static void VarObjAdd(const CMICmnLLDBDebugSessionInfoVarObj &vrVarObj); + static void VarObjDelete(const CMIUtilString &vrVarName); + static bool VarObjGet(const CMIUtilString &vrVarName, + CMICmnLLDBDebugSessionInfoVarObj &vrwVarObj); + static void VarObjUpdate(const CMICmnLLDBDebugSessionInfoVarObj &vrVarObj); + static void VarObjIdInc(); + static MIuint VarObjIdGet(); + static void VarObjIdResetToZero(); + static void VarObjClear(); + static void VarObjSetFormat(varFormat_e eDefaultFormat); + + // Methods: +public: + /* ctor */ CMICmnLLDBDebugSessionInfoVarObj(); + /* ctor */ CMICmnLLDBDebugSessionInfoVarObj( + const CMIUtilString &vrStrNameReal, const CMIUtilString &vrStrName, + const lldb::SBValue &vrValue); + /* ctor */ CMICmnLLDBDebugSessionInfoVarObj( + const CMIUtilString &vrStrNameReal, const CMIUtilString &vrStrName, + const lldb::SBValue &vrValue, const CMIUtilString &vrStrVarObjParentName); + /* ctor */ CMICmnLLDBDebugSessionInfoVarObj( + const CMICmnLLDBDebugSessionInfoVarObj &vrOther); + /* ctor */ CMICmnLLDBDebugSessionInfoVarObj( + CMICmnLLDBDebugSessionInfoVarObj &vrOther); + /* ctor */ CMICmnLLDBDebugSessionInfoVarObj( + CMICmnLLDBDebugSessionInfoVarObj &&vrOther); + // + CMICmnLLDBDebugSessionInfoVarObj & + operator=(const CMICmnLLDBDebugSessionInfoVarObj &vrOther); + CMICmnLLDBDebugSessionInfoVarObj & + operator=(CMICmnLLDBDebugSessionInfoVarObj &&vrwOther); + // + const CMIUtilString &GetName() const; + const CMIUtilString &GetNameReal() const; + const CMIUtilString &GetValueFormatted() const; + lldb::SBValue &GetValue(); + const lldb::SBValue &GetValue() const; + varType_e GetType() const; + bool SetVarFormat(const varFormat_e veVarFormat); + const CMIUtilString &GetVarParentName() const; + void UpdateValue(); + + // Overridden: +public: + // From CMICmnBase + /* dtor */ virtual ~CMICmnLLDBDebugSessionInfoVarObj(); + + // Typedefs: +private: + typedef std::map + MapKeyToVarObj_t; + typedef std::pair + MapPairKeyToVarObj_t; + + // Statics: +private: + static CMIUtilString GetStringFormatted(const MIuint64 vnValue, + const char *vpStrValueNatural, + varFormat_e veVarFormat); + + // Methods: +private: + bool CopyOther(const CMICmnLLDBDebugSessionInfoVarObj &vrOther); + bool MoveOther(CMICmnLLDBDebugSessionInfoVarObj &vrwOther); + + // Attributes: +private: + static const char *ms_aVarFormatStrings[]; + static const char *ms_aVarFormatChars[]; + static MapKeyToVarObj_t ms_mapVarIdToVarObj; + static MIuint ms_nVarUniqueId; + static varFormat_e ms_eDefaultFormat; // overrides "natural" format + // + // *** Update the copy move constructors and assignment operator *** + varFormat_e m_eVarFormat; + varType_e m_eVarType; + CMIUtilString m_strName; + lldb::SBValue m_SBValue; + CMIUtilString m_strNameReal; + CMIUtilString m_strFormattedValue; + CMIUtilString m_strVarObjParentName; + // *** Update the copy move constructors and assignment operator *** +}; diff --git a/src/MICmnLLDBDebugger.cpp b/src/MICmnLLDBDebugger.cpp new file mode 100644 index 00000000000..e5257aaf63f --- /dev/null +++ b/src/MICmnLLDBDebugger.cpp @@ -0,0 +1,908 @@ +//===-- MICmnLLDBDebugger.cpp -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// 2021.3.15 Add support for OHOS target. +// Copyright (c) 2021 Huawei Device Co., Ltd. All rights reserved. +// +//===----------------------------------------------------------------------===// + +// Third party headers: +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBThread.h" +#include "lldb/API/SBType.h" +#include "lldb/API/SBTypeCategory.h" +#include "lldb/API/SBTypeNameSpecifier.h" +#include "lldb/API/SBTypeSummary.h" +#include + +// In-house headers: +#include "MICmnLLDBDebugSessionInfo.h" +#include "MICmnLLDBDebugger.h" +#include "MICmnLLDBDebuggerHandleEvents.h" +#include "MICmnLog.h" +#include "MICmnResources.h" +#include "MICmnThreadMgrStd.h" +#include "MIDriverBase.h" +#include "MIUtilSingletonHelper.h" + +//++ +// MI private summary providers +static inline bool MI_char_summary_provider(lldb::SBValue value, + lldb::SBTypeSummaryOptions options, + lldb::SBStream &stream) { + if (!value.IsValid()) + return false; + + lldb::SBType value_type = value.GetType(); + if (!value_type.IsValid()) + return false; + + lldb::BasicType type_code = value_type.GetBasicType(); + if (type_code == lldb::eBasicTypeSignedChar) + stream.Printf("%d %s", (int)value.GetValueAsSigned(), + CMIUtilString::WithNullAsEmpty(value.GetValue())); + else if (type_code == lldb::eBasicTypeUnsignedChar) + stream.Printf("%u %s", (unsigned)value.GetValueAsUnsigned(), + CMIUtilString::WithNullAsEmpty(value.GetValue())); + else + return false; + + return true; +} + +//++ +// MI summary helper routines +static inline bool MI_add_summary(lldb::SBTypeCategory category, + const char *typeName, + lldb::SBTypeSummary::FormatCallback cb, + uint32_t options, bool regex = false) { +#undef LLDB_DISABLE_PYTHON +#if defined(LLDB_DISABLE_PYTHON) + return false; +#else + lldb::SBTypeSummary summary = + lldb::SBTypeSummary::CreateWithCallback(cb, options); + return summary.IsValid() + ? category.AddTypeSummary( + lldb::SBTypeNameSpecifier(typeName, regex), summary) + : false; +#endif +} + +//++ +// Details: CMICmnLLDBDebugger constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnLLDBDebugger::CMICmnLLDBDebugger() + : m_constStrThisThreadId("MI debugger event") {} + +//++ +// Details: CMICmnLLDBDebugger destructor. +// Type: Overridable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnLLDBDebugger::~CMICmnLLDBDebugger() { Shutdown(); } + +//++ +// Details: Initialize resources for *this debugger object. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugger::Initialize() { + m_clientUsageRefCnt++; + + if (m_bInitialized) + return MIstatus::success; + + bool bOk = MIstatus::success; + CMIUtilString errMsg; + ClrErrorDescription(); + + if (m_pClientDriver == nullptr) { + bOk = false; + errMsg = MIRSRC(IDS_LLDBDEBUGGER_ERR_CLIENTDRIVER); + } + + // Note initialization order is important here as some resources depend on + // previous + MI::ModuleInit(IDS_MI_INIT_ERR_LOG, bOk, errMsg); + MI::ModuleInit(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg); + MI::ModuleInit(IDS_MI_INIT_ERR_THREADMGR, bOk, errMsg); + MI::ModuleInit( + IDS_MI_INIT_ERR_OUTOFBANDHANDLER, bOk, errMsg); + MI::ModuleInit(IDS_MI_INIT_ERR_DEBUGSESSIONINFO, + bOk, errMsg); + + // Note order is important here! + if (bOk) + lldb::SBDebugger::Initialize(); + if (bOk && !InitSBDebugger()) { + bOk = false; + if (!errMsg.empty()) + errMsg += ", "; + errMsg += GetErrorDescription().c_str(); + } + if (bOk && !InitSBListener()) { + bOk = false; + if (!errMsg.empty()) + errMsg += ", "; + errMsg += GetErrorDescription().c_str(); + } + bOk = bOk && InitStdStreams(); + bOk = bOk && RegisterMISummaryProviders(); + m_bInitialized = bOk; + + if (!bOk && !HaveErrorDescription()) { + CMIUtilString strInitError(CMIUtilString::Format( + MIRSRC(IDS_MI_INIT_ERR_LLDBDEBUGGER), errMsg.c_str())); + SetErrorDescription(strInitError); + } + + return bOk; +} + +//++ +// Details: Release resources for *this debugger object. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugger::Shutdown() { + if (--m_clientUsageRefCnt > 0) + return MIstatus::success; + + if (!m_bInitialized) + return MIstatus::success; + + m_bInitialized = false; + + ClrErrorDescription(); + + bool bOk = MIstatus::success; + CMIUtilString errMsg; + + // Explicitly delete the remote target in case MI needs to exit prematurely + // otherwise + // LLDB debugger may hang in its Destroy() fn waiting on events + lldb::SBTarget sbTarget = CMICmnLLDBDebugSessionInfo::Instance().GetTarget(); + m_lldbDebugger.DeleteTarget(sbTarget); + + // Debug: May need this but does seem to work without it so commented out the + // fudge 19/06/2014 + // It appears we need to wait as hang does not occur when hitting a debug + // breakpoint here + // const std::chrono::milliseconds time( 1000 ); + // std::this_thread::sleep_for( time ); + + lldb::SBDebugger::Destroy(m_lldbDebugger); + lldb::SBDebugger::Terminate(); + m_pClientDriver = nullptr; + m_mapBroadcastClassNameToEventMask.clear(); + m_mapIdToEventMask.clear(); + + // Note shutdown order is important here + MI::ModuleShutdown( + IDS_MI_INIT_ERR_DEBUGSESSIONINFO, bOk, errMsg); + MI::ModuleShutdown( + IDS_MI_INIT_ERR_OUTOFBANDHANDLER, bOk, errMsg); + MI::ModuleShutdown(IDS_MI_INIT_ERR_THREADMGR, bOk, + errMsg); + MI::ModuleShutdown(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg); + MI::ModuleShutdown(IDS_MI_INIT_ERR_LOG, bOk, errMsg); + + if (!bOk) { + SetErrorDescriptionn(MIRSRC(IDS_MI_SHTDWN_ERR_LLDBDEBUGGER), + errMsg.c_str()); + } + + return MIstatus::success; +} + +//++ +// Details: Return the LLDB debugger instance created for this debug session. +// Type: Method. +// Args: None. +// Return: lldb::SBDebugger & - LLDB debugger object reference. +// Throws: None. +//-- +lldb::SBDebugger &CMICmnLLDBDebugger::GetTheDebugger() { + return m_lldbDebugger; +} + +//++ +// Details: Return the LLDB listener instance created for this debug session. +// Type: Method. +// Args: None. +// Return: lldb::SBListener & - LLDB listener object reference. +// Throws: None. +//-- +lldb::SBListener &CMICmnLLDBDebugger::GetTheListener() { + return m_lldbListener; +} + +//++ +// Details: Set the client driver that wants to use *this LLDB debugger. Call +// this function +// prior to Initialize(). +// Type: Method. +// Args: vClientDriver - (R) A driver. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugger::SetDriver(const CMIDriverBase &vClientDriver) { + m_pClientDriver = const_cast(&vClientDriver); + + return MIstatus::success; +} + +//++ +// Details: Get the client driver that is use *this LLDB debugger. +// Type: Method. +// Args: vClientDriver - (R) A driver. +// Return: CMIDriverBase & - A driver instance. +// Throws: None. +//-- +CMIDriverBase &CMICmnLLDBDebugger::GetDriver() const { + return *m_pClientDriver; +} + +//++ +// Details: Wait until all events have been handled. +// This function works in pair with +// CMICmnLLDBDebugger::MonitorSBListenerEvents +// that handles events from queue. When all events were handled and +// queue is +// empty the MonitorSBListenerEvents notifies this function that it's +// ready to +// go on. To synchronize them the m_mutexEventQueue and +// m_conditionEventQueueEmpty are used. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMICmnLLDBDebugger::WaitForHandleEvent() { + std::unique_lock lock(m_mutexEventQueue); + + lldb::SBEvent event; + if (ThreadIsActive() && m_lldbListener.PeekAtNextEvent(event)) + m_conditionEventQueueEmpty.wait(lock); +} + +//++ +// Details: Check if need to rebroadcast stop event. This function will return +// true if +// debugger is in synchronouse mode. In such case the +// CMICmnLLDBDebugger::RebroadcastStopEvent should be called to +// rebroadcast +// a new stop event (if any). +// Type: Method. +// Args: None. +// Return: bool - True = Need to rebroadcast stop event, false = otherwise. +// Throws: None. +//-- +bool CMICmnLLDBDebugger::CheckIfNeedToRebroadcastStopEvent() { + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + if (!rSessionInfo.GetDebugger().GetAsync()) { + const bool include_expression_stops = false; + m_nLastStopId = + CMICmnLLDBDebugSessionInfo::Instance().GetProcess().GetStopID( + include_expression_stops); + return true; + } + + return false; +} + +//++ +// Details: Rebroadcast stop event if needed. This function should be called +// only if the +// CMICmnLLDBDebugger::CheckIfNeedToRebroadcastStopEvent() returned +// true. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMICmnLLDBDebugger::RebroadcastStopEvent() { + lldb::SBProcess process = CMICmnLLDBDebugSessionInfo::Instance().GetProcess(); + const bool include_expression_stops = false; + const uint32_t nStopId = process.GetStopID(include_expression_stops); + if (m_nLastStopId != nStopId) { + lldb::SBEvent event = process.GetStopEventForStopID(nStopId); + process.GetBroadcaster().BroadcastEvent(event); + } +} + +//++ +// Details: Initialize the LLDB Debugger object. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugger::InitSBDebugger() { + m_lldbDebugger = lldb::SBDebugger::Create(false); + if (!m_lldbDebugger.IsValid()) { + SetErrorDescription(MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDDEBUGGER)); + return MIstatus::failure; + } + + m_lldbDebugger.GetCommandInterpreter().SetPromptOnQuit(false); + + return MIstatus::success; +} + +//++ +// Details: Set the LLDB Debugger's std in, err and out streams. (Not +// implemented left +// here for reference. Was called in the +// CMICmnLLDBDebugger::Initialize() ) +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugger::InitStdStreams() { + // This is not required when operating the MI driver's code as it has its own + // streams. Setting the Stdin for the lldbDebugger especially on LINUX will + // cause + // another thread to run and partially consume stdin data meant for MI stdin + // handler + // m_lldbDebugger.SetErrorFileHandle( m_pClientDriver->GetStderr(), false ); + // m_lldbDebugger.SetOutputFileHandle( m_pClientDriver->GetStdout(), false ); + // m_lldbDebugger.SetInputFileHandle( m_pClientDriver->GetStdin(), false ); + + return MIstatus::success; +} + +//++ +// Details: Set up the events from the SBDebugger's we would like to listen to. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugger::InitSBListener() { + m_lldbListener = m_lldbDebugger.GetListener(); + if (!m_lldbListener.IsValid()) { + SetErrorDescription(MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDLISTENER)); + return MIstatus::failure; + } + + const CMIUtilString strDbgId("CMICmnLLDBDebugger1"); + MIuint eventMask = lldb::SBTarget::eBroadcastBitBreakpointChanged | + lldb::SBTarget::eBroadcastBitModulesLoaded | + lldb::SBTarget::eBroadcastBitModulesUnloaded | + lldb::SBTarget::eBroadcastBitWatchpointChanged | + lldb::SBTarget::eBroadcastBitSymbolsLoaded; + bool bOk = RegisterForEvent( + strDbgId, CMIUtilString(lldb::SBTarget::GetBroadcasterClassName()), + eventMask); + + eventMask = lldb::SBThread::eBroadcastBitStackChanged; + bOk = bOk && + RegisterForEvent( + strDbgId, CMIUtilString(lldb::SBThread::GetBroadcasterClassName()), + eventMask); + + eventMask = lldb::SBProcess::eBroadcastBitStateChanged | + lldb::SBProcess::eBroadcastBitInterrupt | + lldb::SBProcess::eBroadcastBitSTDOUT | + lldb::SBProcess::eBroadcastBitSTDERR | + lldb::SBProcess::eBroadcastBitProfileData | + lldb::SBProcess::eBroadcastBitStructuredData; + bOk = bOk && + RegisterForEvent( + strDbgId, CMIUtilString(lldb::SBProcess::GetBroadcasterClassName()), + eventMask); + + eventMask = lldb::SBCommandInterpreter::eBroadcastBitQuitCommandReceived | + lldb::SBCommandInterpreter::eBroadcastBitThreadShouldExit | + lldb::SBCommandInterpreter::eBroadcastBitAsynchronousOutputData | + lldb::SBCommandInterpreter::eBroadcastBitAsynchronousErrorData; + bOk = bOk && + RegisterForEvent( + strDbgId, m_lldbDebugger.GetCommandInterpreter().GetBroadcaster(), + eventMask); + + return bOk; +} + +//++ +// Details: Register with the debugger, the SBListener, the type of events you +// are interested +// in. Others, like commands, may have already set the mask. +// Type: Method. +// Args: vClientName - (R) ID of the client who wants these events +// set. +// vBroadcasterClass - (R) The SBBroadcaster's class name. +// vEventMask - (R) The mask of events to listen for. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugger::RegisterForEvent( + const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass, + const MIuint vEventMask) { + MIuint existingMask = 0; + if (!BroadcasterGetMask(vBroadcasterClass, existingMask)) + return MIstatus::failure; + + if (!ClientSaveMask(vClientName, vBroadcasterClass, vEventMask)) + return MIstatus::failure; + + const char *pBroadCasterName = vBroadcasterClass.c_str(); + MIuint eventMask = vEventMask; + eventMask += existingMask; + const MIuint result = m_lldbListener.StartListeningForEventClass( + m_lldbDebugger, pBroadCasterName, eventMask); + if (result == 0) { + SetErrorDescription(CMIUtilString::Format( + MIRSRC(IDS_LLDBDEBUGGER_ERR_STARTLISTENER), pBroadCasterName)); + return MIstatus::failure; + } + + return BroadcasterSaveMask(vBroadcasterClass, eventMask); +} + +//++ +// Details: Register with the debugger, the SBListener, the type of events you +// are interested +// in. Others, like commands, may have already set the mask. +// Type: Method. +// Args: vClientName - (R) ID of the client who wants these events set. +// vBroadcaster - (R) An SBBroadcaster's derived class. +// vEventMask - (R) The mask of events to listen for. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugger::RegisterForEvent( + const CMIUtilString &vClientName, const lldb::SBBroadcaster &vBroadcaster, + const MIuint vEventMask) { + const char *pBroadcasterName = vBroadcaster.GetName(); + if (pBroadcasterName == nullptr) { + SetErrorDescription( + CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_BROADCASTER_NAME), + MIRSRC(IDS_WORD_INVALIDNULLPTR))); + return MIstatus::failure; + } + CMIUtilString broadcasterName(pBroadcasterName); + if (broadcasterName.length() == 0) { + SetErrorDescription( + CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_BROADCASTER_NAME), + MIRSRC(IDS_WORD_INVALIDEMPTY))); + return MIstatus::failure; + } + + MIuint existingMask = 0; + if (!BroadcasterGetMask(broadcasterName, existingMask)) + return MIstatus::failure; + + if (!ClientSaveMask(vClientName, broadcasterName, vEventMask)) + return MIstatus::failure; + + MIuint eventMask = vEventMask; + eventMask += existingMask; + const MIuint result = + m_lldbListener.StartListeningForEvents(vBroadcaster, eventMask); + if (result == 0) { + SetErrorDescription(CMIUtilString::Format( + MIRSRC(IDS_LLDBDEBUGGER_ERR_STARTLISTENER), pBroadcasterName)); + return MIstatus::failure; + } + + return BroadcasterSaveMask(broadcasterName, eventMask); +} + +//++ +// Details: Unregister with the debugger, the SBListener, the type of events you +// are no +// longer interested in. Others, like commands, may still remain +// interested so +// an event may not necessarily be stopped. +// Type: Method. +// Args: vClientName - (R) ID of the client who no longer requires +// these events. +// vBroadcasterClass - (R) The SBBroadcaster's class name. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugger::UnregisterForEvent( + const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass) { + MIuint clientsEventMask = 0; + if (!ClientGetTheirMask(vClientName, vBroadcasterClass, clientsEventMask)) + return MIstatus::failure; + if (!ClientRemoveTheirMask(vClientName, vBroadcasterClass)) + return MIstatus::failure; + + const MIuint otherClientsEventMask = + ClientGetMaskForAllClients(vBroadcasterClass); + MIuint newEventMask = 0; + for (MIuint i = 0; i < 32; i++) { + const MIuint bit = MIuint(1) << i; + const MIuint clientBit = bit & clientsEventMask; + const MIuint othersBit = bit & otherClientsEventMask; + if ((clientBit != 0) && (othersBit == 0)) { + newEventMask += clientBit; + } + } + + const char *pBroadCasterName = vBroadcasterClass.c_str(); + if (!m_lldbListener.StopListeningForEventClass( + m_lldbDebugger, pBroadCasterName, newEventMask)) { + SetErrorDescription( + CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_STOPLISTENER), + vClientName.c_str(), pBroadCasterName)); + return MIstatus::failure; + } + + return BroadcasterSaveMask(vBroadcasterClass, otherClientsEventMask); +} + +//++ +// Details: Given the SBBroadcaster class name retrieve it's current event mask. +// Type: Method. +// Args: vBroadcasterClass - (R) The SBBroadcaster's class name. +// vEventMask - (W) The mask of events to listen for. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugger::BroadcasterGetMask( + const CMIUtilString &vBroadcasterClass, MIuint &vwEventMask) const { + vwEventMask = 0; + + if (vBroadcasterClass.empty()) { + SetErrorDescription( + CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDBROADCASTER), + vBroadcasterClass.c_str())); + return MIstatus::failure; + } + + const MapBroadcastClassNameToEventMask_t::const_iterator it = + m_mapBroadcastClassNameToEventMask.find(vBroadcasterClass); + if (it != m_mapBroadcastClassNameToEventMask.end()) { + vwEventMask = (*it).second; + } + + return MIstatus::success; +} + +//++ +// Details: Remove the event mask for the specified SBBroadcaster class name. +// Type: Method. +// Args: vBroadcasterClass - (R) The SBBroadcaster's class name. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugger::BroadcasterRemoveMask( + const CMIUtilString &vBroadcasterClass) { + MapBroadcastClassNameToEventMask_t::const_iterator it = + m_mapBroadcastClassNameToEventMask.find(vBroadcasterClass); + if (it != m_mapBroadcastClassNameToEventMask.end()) { + m_mapBroadcastClassNameToEventMask.erase(it); + } + + return MIstatus::success; +} + +//++ +// Details: Given the SBBroadcaster class name save it's current event mask. +// Type: Method. +// Args: vBroadcasterClass - (R) The SBBroadcaster's class name. +// vEventMask - (R) The mask of events to listen for. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugger::BroadcasterSaveMask( + const CMIUtilString &vBroadcasterClass, const MIuint vEventMask) { + if (vBroadcasterClass.empty()) { + SetErrorDescription( + CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDBROADCASTER), + vBroadcasterClass.c_str())); + return MIstatus::failure; + } + + BroadcasterRemoveMask(vBroadcasterClass); + MapPairBroadcastClassNameToEventMask_t pr(vBroadcasterClass, vEventMask); + m_mapBroadcastClassNameToEventMask.insert(pr); + + return MIstatus::success; +} + +//++ +// Details: Iterate all the clients who have registered event masks against +// particular +// SBBroadcasters and build up the mask that is for all of them. +// Type: Method. +// Args: vBroadcasterClass - (R) The broadcaster to retrieve the mask for. +// Return: MIuint - Event mask. +// Throws: None. +//-- +MIuint CMICmnLLDBDebugger::ClientGetMaskForAllClients( + const CMIUtilString &vBroadcasterClass) const { + MIuint mask = 0; + MapIdToEventMask_t::const_iterator it = m_mapIdToEventMask.begin(); + while (it != m_mapIdToEventMask.end()) { + const CMIUtilString &rId((*it).first); + if (rId.find(vBroadcasterClass) != std::string::npos) { + const MIuint clientsMask = (*it).second; + mask |= clientsMask; + } + + // Next + ++it; + } + + return mask; +} + +//++ +// Details: Given the client save its particular event requirements. +// Type: Method. +// Args: vClientName - (R) The Client's unique ID. +// vBroadcasterClass - (R) The SBBroadcaster's class name targeted for +// the events. +// vEventMask - (R) The mask of events to listen for. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugger::ClientSaveMask(const CMIUtilString &vClientName, + const CMIUtilString &vBroadcasterClass, + const MIuint vEventMask) { + if (vClientName.empty()) { + SetErrorDescription(CMIUtilString::Format( + MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDCLIENTNAME), vClientName.c_str())); + return MIstatus::failure; + } + + CMIUtilString strId(vBroadcasterClass); + strId += vClientName; + + ClientRemoveTheirMask(vClientName, vBroadcasterClass); + MapPairIdToEventMask_t pr(strId, vEventMask); + m_mapIdToEventMask.insert(pr); + + return MIstatus::success; +} + +//++ +// Details: Given the client remove it's particular event requirements. +// Type: Method. +// Args: vClientName - (R) The Client's unique ID. +// vBroadcasterClass - (R) The SBBroadcaster's class name. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugger::ClientRemoveTheirMask( + const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass) { + if (vClientName.empty()) { + SetErrorDescription(CMIUtilString::Format( + MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDCLIENTNAME), vClientName.c_str())); + return MIstatus::failure; + } + + CMIUtilString strId(vBroadcasterClass); + strId += vClientName; + + const MapIdToEventMask_t::const_iterator it = m_mapIdToEventMask.find(strId); + if (it != m_mapIdToEventMask.end()) { + m_mapIdToEventMask.erase(it); + } + + return MIstatus::success; +} + +//++ +// Details: Retrieve the client's event mask used for on a particular +// SBBroadcaster. +// Type: Method. +// Args: vClientName - (R) The Client's unique ID. +// vBroadcasterClass - (R) The SBBroadcaster's class name. +// vwEventMask - (W) The client's mask. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugger::ClientGetTheirMask( + const CMIUtilString &vClientName, const CMIUtilString &vBroadcasterClass, + MIuint &vwEventMask) { + vwEventMask = 0; + + if (vClientName.empty()) { + SetErrorDescription(CMIUtilString::Format( + MIRSRC(IDS_LLDBDEBUGGER_ERR_INVALIDCLIENTNAME), vClientName.c_str())); + return MIstatus::failure; + } + + const CMIUtilString strId(vBroadcasterClass + vClientName); + const MapIdToEventMask_t::const_iterator it = m_mapIdToEventMask.find(strId); + if (it != m_mapIdToEventMask.end()) { + vwEventMask = (*it).second; + } + + SetErrorDescription(CMIUtilString::Format( + MIRSRC(IDS_LLDBDEBUGGER_ERR_CLIENTNOTREGISTERED), vClientName.c_str())); + + return MIstatus::failure; +} + +//++ +// Details: Momentarily wait for an events being broadcast and inspect those +// that do +// come this way. Check if the target should exit event if so start +// shutting +// down this thread and the application. Any other events pass on to +// the +// Out-of-band handler to further determine what kind of event arrived. +// This function runs in the thread "MI debugger event". +// Type: Method. +// Args: vrbIsAlive - (W) False = yes exit event monitoring thread, true = +// continue. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugger::MonitorSBListenerEvents(bool &vrbIsAlive) { + vrbIsAlive = true; + + // Lock the mutex of event queue + // Note that it should be locked while we are in + // CMICmnLLDBDebugger::MonitorSBListenerEvents to + // avoid a race condition with CMICmnLLDBDebugger::WaitForHandleEvent + std::unique_lock lock(m_mutexEventQueue); + + lldb::SBEvent event; + const bool bGotEvent = m_lldbListener.GetNextEvent(event); + if (!bGotEvent) { + // Notify that we are finished and unlock the mutex of event queue before + // sleeping + m_conditionEventQueueEmpty.notify_one(); + lock.unlock(); + + // Wait a bit to reduce CPU load + const std::chrono::milliseconds time(1); + std::this_thread::sleep_for(time); + return MIstatus::success; + } + assert(event.IsValid()); + assert(event.GetBroadcaster().IsValid()); + + // Debugging + m_pLog->WriteLog(CMIUtilString::Format("##### An event occurred: %s", + event.GetBroadcasterClass())); + + bool bHandledEvent = false; + bool bOk = false; + { + // Lock Mutex before handling events so that we don't disturb a running cmd + CMIUtilThreadLock lock( + CMICmnLLDBDebugSessionInfo::Instance().GetSessionMutex()); + bOk = CMICmnLLDBDebuggerHandleEvents::Instance().HandleEvent(event, + bHandledEvent); + } + + if (!bHandledEvent) { + const CMIUtilString msg( + CMIUtilString::Format(MIRSRC(IDS_LLDBDEBUGGER_WRN_UNKNOWN_EVENT), + event.GetBroadcasterClass())); + m_pLog->WriteLog(msg); + } + + if (!bOk) + m_pLog->WriteLog( + CMICmnLLDBDebuggerHandleEvents::Instance().GetErrorDescription()); + + return MIstatus::success; +} + +//++ +// Details: The main worker method for this thread. +// Type: Method. +// Args: vrbIsAlive - (W) True = *this thread is working, false = thread has +// exited. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugger::ThreadRun(bool &vrbIsAlive) { + return MonitorSBListenerEvents(vrbIsAlive); +} + +//++ +// Details: Let this thread clean up after itself. +// Type: Method. +// Args: +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugger::ThreadFinish() { return MIstatus::success; } + +//++ +// Details: Retrieve *this thread object's name. +// Type: Overridden. +// Args: None. +// Return: CMIUtilString & - Text. +// Throws: None. +//-- +const CMIUtilString &CMICmnLLDBDebugger::ThreadGetName() const { + return m_constStrThisThreadId; +} + +//++ +// Details: Loads lldb-mi formatters +// Type: Method. +// Args: None. +// Return: true - Functionality succeeded. +// false - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugger::LoadMIFormatters(lldb::SBTypeCategory miCategory) { + if (!MI_add_summary(miCategory, "char", MI_char_summary_provider, + lldb::eTypeOptionHideValue | + lldb::eTypeOptionSkipPointers)) + return false; + + if (!MI_add_summary(miCategory, "unsigned char", MI_char_summary_provider, + lldb::eTypeOptionHideValue | + lldb::eTypeOptionSkipPointers)) + return false; + + if (!MI_add_summary(miCategory, "signed char", MI_char_summary_provider, + lldb::eTypeOptionHideValue | + lldb::eTypeOptionSkipPointers)) + return false; + + return true; +} + +//++ +// Details: Registers lldb-mi custom summary providers +// Type: Method. +// Args: None. +// Return: true - Functionality succeeded. +// false - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebugger::RegisterMISummaryProviders() { + static const char *miCategoryName = "lldb-mi"; + lldb::SBTypeCategory miCategory = + m_lldbDebugger.CreateCategory(miCategoryName); + if (!miCategory.IsValid()) + return false; + + if (!LoadMIFormatters(miCategory)) { + m_lldbDebugger.DeleteCategory(miCategoryName); + return false; + } + miCategory.SetEnabled(true); + return true; +} diff --git a/src/MICmnLLDBDebugger.h b/src/MICmnLLDBDebugger.h new file mode 100644 index 00000000000..29cf9416e15 --- /dev/null +++ b/src/MICmnLLDBDebugger.h @@ -0,0 +1,134 @@ +//===-- MICmnLLDBDebugger.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// Third party headers +#include "lldb/API/SBDebugger.h" +#include "lldb/API/SBEvent.h" +#include "lldb/API/SBListener.h" +#include +#include +#include + +// In-house headers: +#include "MICmnBase.h" +#include "MIUtilSingletonBase.h" +#include "MIUtilThreadBaseStd.h" + +// Declarations: +class CMIDriverBase; +class CMICmnLLDBDebuggerHandleEvents; + +//++ +//============================================================================ +// Details: MI proxy/adapter for the LLDB public SBDebugger API. The CMIDriver +// requires *this object. Command classes make calls on *this object +// to facilitate their work effort. The instance runs in its own worker +// thread. +// A singleton class. +//-- +class CMICmnLLDBDebugger : public CMICmnBase, + public CMIUtilThreadActiveObjBase, + public MI::ISingleton { + friend class MI::ISingleton; + + // Methods: +public: + bool Initialize() override; + bool Shutdown() override; + + bool SetDriver(const CMIDriverBase &vClientDriver); + CMIDriverBase &GetDriver() const; + lldb::SBDebugger &GetTheDebugger(); + lldb::SBListener &GetTheListener(); + void WaitForHandleEvent(); + bool CheckIfNeedToRebroadcastStopEvent(); + void RebroadcastStopEvent(); + + // MI Commands can use these functions to listen for events they require + bool RegisterForEvent(const CMIUtilString &vClientName, + const CMIUtilString &vBroadcasterClass, + const MIuint vEventMask); + bool UnregisterForEvent(const CMIUtilString &vClientName, + const CMIUtilString &vBroadcasterClass); + bool RegisterForEvent(const CMIUtilString &vClientName, + const lldb::SBBroadcaster &vBroadcaster, + const MIuint vEventMask); + bool UnregisterForEvent(const CMIUtilString &vClientName, + const lldb::SBBroadcaster &vBroadcaster); + + // Overridden: +public: + // From CMIUtilThreadActiveObjBase + const CMIUtilString &ThreadGetName() const override; + + // Overridden: +protected: + // From CMIUtilThreadActiveObjBase + bool ThreadRun(bool &vrIsAlive) override; + bool ThreadFinish() override; + + // Typedefs: +private: + typedef std::map MapBroadcastClassNameToEventMask_t; + typedef std::pair + MapPairBroadcastClassNameToEventMask_t; + typedef std::map MapIdToEventMask_t; + typedef std::pair MapPairIdToEventMask_t; + + // Methods: +private: + /* ctor */ CMICmnLLDBDebugger(); + /* ctor */ CMICmnLLDBDebugger(const CMICmnLLDBDebugger &); + void operator=(const CMICmnLLDBDebugger &); + + bool InitSBDebugger(); + bool InitSBListener(); + bool InitStdStreams(); + bool MonitorSBListenerEvents(bool &vrbYesExit); + + bool BroadcasterGetMask(const CMIUtilString &vBroadcasterClass, + MIuint &vEventMask) const; + bool BroadcasterRemoveMask(const CMIUtilString &vBroadcasterClass); + bool BroadcasterSaveMask(const CMIUtilString &vBroadcasterClass, + const MIuint vEventMask); + + MIuint + ClientGetMaskForAllClients(const CMIUtilString &vBroadcasterClass) const; + bool ClientSaveMask(const CMIUtilString &vClientName, + const CMIUtilString &vBroadcasterClass, + const MIuint vEventMask); + bool ClientRemoveTheirMask(const CMIUtilString &vClientName, + const CMIUtilString &vBroadcasterClass); + bool ClientGetTheirMask(const CMIUtilString &vClientName, + const CMIUtilString &vBroadcasterClass, + MIuint &vwEventMask); + bool LoadMIFormatters(lldb::SBTypeCategory miCategory); + bool RegisterMISummaryProviders(); + // Overridden: +private: + // From CMICmnBase + /* dtor */ ~CMICmnLLDBDebugger() override; + + // Attributes: +private: + CMIDriverBase + *m_pClientDriver; // The driver that wants to use *this LLDB debugger + lldb::SBDebugger m_lldbDebugger; // SBDebugger is the primordial object that + // creates SBTargets and provides access to + // them + lldb::SBListener m_lldbListener; // API clients can register its own listener + // to debugger events + const CMIUtilString m_constStrThisThreadId; + MapBroadcastClassNameToEventMask_t m_mapBroadcastClassNameToEventMask; + MapIdToEventMask_t m_mapIdToEventMask; + std::mutex m_mutexEventQueue; + std::condition_variable m_conditionEventQueueEmpty; + uint32_t m_nLastStopId; +}; diff --git a/src/MICmnLLDBDebuggerHandleEvents.cpp b/src/MICmnLLDBDebuggerHandleEvents.cpp new file mode 100644 index 00000000000..47e7a25394d --- /dev/null +++ b/src/MICmnLLDBDebuggerHandleEvents.cpp @@ -0,0 +1,2017 @@ +//===-- MICmnLLDBDebuggerHandleEvents.cpp -----------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// 2021.3.15 Add support for OHOS target. +// Copyright (c) 2021 Huawei Device Co., Ltd. All rights reserved. +// +//===----------------------------------------------------------------------===// + +// Third party headers: +#include "lldb/API/SBAddress.h" +#include "lldb/API/SBBreakpoint.h" +#include "lldb/API/SBCommandInterpreter.h" +#include "lldb/API/SBCommandReturnObject.h" +#include "lldb/API/SBEvent.h" +#include "lldb/API/SBProcess.h" +#include "lldb/API/SBStream.h" +#include "lldb/API/SBTarget.h" +#include "lldb/API/SBThread.h" +#include "lldb/API/SBUnixSignals.h" +#include "llvm/Support/Compiler.h" +#ifdef _WIN32 +#include +#else +#include +#endif // _WIN32 + +// In-house headers: +#include "MICmnLLDBDebugSessionInfo.h" +#include "MICmnLLDBDebugger.h" +#include "MICmnLLDBDebuggerHandleEvents.h" +#include "MICmnLog.h" +#include "MICmnMIOutOfBandRecord.h" +#include "MICmnMIResultRecord.h" +#include "MICmnMIValueConst.h" +#include "MICmnMIValueList.h" +#include "MICmnResources.h" +#include "MICmnStreamStderr.h" +#include "MICmnStreamStdout.h" +#include "MIDriver.h" +#include "MIUtilDebug.h" +#include "Platform.h" + +#include + +//++ +// Details: CMICmnLLDBDebuggerHandleEvents constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnLLDBDebuggerHandleEvents::CMICmnLLDBDebuggerHandleEvents() {} + +//++ +// Details: CMICmnLLDBDebuggerHandleEvents destructor. +// Type: Overridable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnLLDBDebuggerHandleEvents::~CMICmnLLDBDebuggerHandleEvents() { + Shutdown(); +} + +//++ +// Details: Initialize resources for *this broadcaster object. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::Initialize() { + m_clientUsageRefCnt++; + + if (m_bInitialized) + return MIstatus::success; + + m_bInitialized = MIstatus::success; + m_bSignalsInitialized = false; + m_SIGINT = 0; + m_SIGSTOP = 0; + m_SIGSEGV = 0; + m_SIGTRAP = 0; + + return m_bInitialized; +} + +//++ +// Details: Release resources for *this broadcaster object. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::Shutdown() { + if (--m_clientUsageRefCnt > 0) + return MIstatus::success; + + if (!m_bInitialized) + return MIstatus::success; + + m_bInitialized = false; + + return MIstatus::success; +} + +//++ +// Details: Interpret the event object to ascertain the action to take or +// information to +// to form and put in a MI Out-of-band record object which is given to +// stdout. +// Type: Method. +// Args: vEvent - (R) An LLDB broadcast event. +// vrbHandledEvent - (W) True - event handled, false = not handled. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::HandleEvent(const lldb::SBEvent &vEvent, + bool &vrbHandledEvent) { + bool bOk = MIstatus::success; + vrbHandledEvent = false; + + if (lldb::SBProcess::EventIsProcessEvent(vEvent)) { + vrbHandledEvent = true; + bOk = HandleEventSBProcess(vEvent); + } else if (lldb::SBBreakpoint::EventIsBreakpointEvent(vEvent)) { + vrbHandledEvent = true; + bOk = HandleEventSBBreakPoint(vEvent); + } else if (lldb::SBThread::EventIsThreadEvent(vEvent)) { + vrbHandledEvent = true; + bOk = HandleEventSBThread(vEvent); + } else if (lldb::SBTarget::EventIsTargetEvent(vEvent)) { + vrbHandledEvent = true; + bOk = HandleEventSBTarget(vEvent); + } else if (lldb::SBCommandInterpreter::EventIsCommandInterpreterEvent( + vEvent)) { + vrbHandledEvent = true; + bOk = HandleEventSBCommandInterpreter(vEvent); + } + + return bOk; +} + +//++ +// Details: Handle a LLDB SBProcess event. +// Type: Method. +// Args: vEvent - (R) An LLDB broadcast event. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::HandleEventSBProcess( + const lldb::SBEvent &vEvent) { + bool bOk = MIstatus::success; + + const char *pEventType = ""; + const MIuint nEventType = vEvent.GetType(); + switch (nEventType) { + case lldb::SBProcess::eBroadcastBitInterrupt: + pEventType = "eBroadcastBitInterrupt"; + break; + case lldb::SBProcess::eBroadcastBitProfileData: + pEventType = "eBroadcastBitProfileData"; + break; + case lldb::SBProcess::eBroadcastBitStructuredData: + pEventType = "eBroadcastBitStructuredData"; + break; + case lldb::SBProcess::eBroadcastBitStateChanged: + pEventType = "eBroadcastBitStateChanged"; + bOk = HandleProcessEventBroadcastBitStateChanged(vEvent); + break; + case lldb::SBProcess::eBroadcastBitSTDERR: + pEventType = "eBroadcastBitSTDERR"; + bOk = GetProcessStderr(); + break; + case lldb::SBProcess::eBroadcastBitSTDOUT: + pEventType = "eBroadcastBitSTDOUT"; + bOk = GetProcessStdout(); + break; + default: { + const CMIUtilString msg( + CMIUtilString::Format(MIRSRC(IDS_LLDBOUTOFBAND_ERR_UNKNOWN_EVENT), + "SBProcess", (MIuint)nEventType)); + SetErrorDescription(msg); + return MIstatus::failure; + } + } + m_pLog->WriteLog(CMIUtilString::Format( + "##### An SB Process event occurred: %s", pEventType)); + + return bOk; +} + +//++ +// Details: Handle a LLDB SBBreakpoint event. +// Type: Method. +// Args: vEvent - (R) An LLDB broadcast event. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::HandleEventSBBreakPoint( + const lldb::SBEvent &vEvent) { + bool bOk = MIstatus::success; + + const char *pEventType = ""; + const lldb::BreakpointEventType eEvent = + lldb::SBBreakpoint::GetBreakpointEventTypeFromEvent(vEvent); + switch (eEvent) { + case lldb::eBreakpointEventTypeThreadChanged: + pEventType = "eBreakpointEventTypeThreadChanged"; + break; + case lldb::eBreakpointEventTypeLocationsRemoved: + pEventType = "eBreakpointEventTypeLocationsRemoved"; + break; + case lldb::eBreakpointEventTypeInvalidType: + pEventType = "eBreakpointEventTypeInvalidType"; + break; + case lldb::eBreakpointEventTypeLocationsAdded: + pEventType = "eBreakpointEventTypeLocationsAdded"; + bOk = HandleEventSBBreakpointLocationsAdded(vEvent); + break; + case lldb::eBreakpointEventTypeAdded: + pEventType = "eBreakpointEventTypeAdded"; + bOk = HandleEventSBBreakpointAdded(vEvent); + break; + case lldb::eBreakpointEventTypeRemoved: + pEventType = "eBreakpointEventTypeRemoved"; + bOk = HandleEventSBBreakpointCmn(vEvent); + break; + case lldb::eBreakpointEventTypeLocationsResolved: + pEventType = "eBreakpointEventTypeLocationsResolved"; + bOk = HandleEventSBBreakpointCmn(vEvent); + break; + case lldb::eBreakpointEventTypeEnabled: + pEventType = "eBreakpointEventTypeEnabled"; + bOk = HandleEventSBBreakpointCmn(vEvent); + break; + case lldb::eBreakpointEventTypeDisabled: + pEventType = "eBreakpointEventTypeDisabled"; + bOk = HandleEventSBBreakpointCmn(vEvent); + break; + case lldb::eBreakpointEventTypeCommandChanged: + pEventType = "eBreakpointEventTypeCommandChanged"; + bOk = HandleEventSBBreakpointCmn(vEvent); + break; + case lldb::eBreakpointEventTypeConditionChanged: + pEventType = "eBreakpointEventTypeConditionChanged"; + bOk = HandleEventSBBreakpointCmn(vEvent); + break; + case lldb::eBreakpointEventTypeIgnoreChanged: + pEventType = "eBreakpointEventTypeIgnoreChanged"; + bOk = HandleEventSBBreakpointCmn(vEvent); + break; + case lldb::eBreakpointEventTypeAutoContinueChanged: + pEventType = "eBreakpointEventTypeAutoContinueChanged"; + bOk = HandleEventSBBreakpointCmn(vEvent); + break; + } + m_pLog->WriteLog(CMIUtilString::Format( + "##### An SB Breakpoint event occurred: %s", pEventType)); + + return bOk; +} + +//++ +// Details: Handle a LLDB SBBreakpoint event. +// Type: Method. +// Args: vEvent - (R) An LLDB broadcast event. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::HandleEventSBBreakpointLocationsAdded( + const lldb::SBEvent &vEvent) { + const MIuint nLoc = + lldb::SBBreakpoint::GetNumBreakpointLocationsFromEvent(vEvent); + if (nLoc == 0) + return MIstatus::success; + + lldb::SBBreakpoint brkPt = lldb::SBBreakpoint::GetBreakpointFromEvent(vEvent); + const CMIUtilString plural((nLoc == 1) ? "" : "s"); + const CMIUtilString msg( + CMIUtilString::Format("%d location%s added to breakpoint %d", nLoc, + plural.c_str(), brkPt.GetID())); + + return TextToStdout(msg); +} + +//++ +// Details: Handle a LLDB SBBreakpoint event. +// Type: Method. +// Args: vEvent - (R) An LLDB broadcast event. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::HandleEventSBBreakpointCmn( + const lldb::SBEvent &vEvent) { + lldb::SBBreakpoint brkPt = lldb::SBBreakpoint::GetBreakpointFromEvent(vEvent); + if (!brkPt.IsValid()) + return MIstatus::success; + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + CMICmnLLDBDebugSessionInfo::SBrkPtInfo sBrkPtInfo; + if (!rSessionInfo.GetBrkPtInfo(brkPt, sBrkPtInfo)) { + SetErrorDescription( + CMIUtilString::Format(MIRSRC(IDS_LLDBOUTOFBAND_ERR_BRKPT_INFO_GET), + "HandleEventSBBreakpointCmn()", brkPt.GetID())); + return MIstatus::failure; + } + + // CODETAG_LLDB_BREAKPOINT_CREATION + // This is in a worker thread + // Add more breakpoint information or overwrite existing information + CMICmnLLDBDebugSessionInfo::SBrkPtInfo sBrkPtInfoRec; + if (!rSessionInfo.RecordBrkPtInfoGet(brkPt.GetID(), sBrkPtInfoRec)) { + SetErrorDescription( + CMIUtilString::Format(MIRSRC(IDS_LLDBOUTOFBAND_ERR_BRKPT_NOTFOUND), + "HandleEventSBBreakpointCmn()", brkPt.GetID())); + return MIstatus::failure; + } + sBrkPtInfo.m_bDisp = sBrkPtInfoRec.m_bDisp; + sBrkPtInfo.m_bEnabled = brkPt.IsEnabled(); + sBrkPtInfo.m_bHaveArgOptionThreadGrp = false; + sBrkPtInfo.m_strOptThrdGrp = ""; + sBrkPtInfo.m_nTimes = brkPt.GetHitCount(); + sBrkPtInfo.m_strOrigLoc = sBrkPtInfoRec.m_strOrigLoc; + sBrkPtInfo.m_nIgnore = sBrkPtInfoRec.m_nIgnore; + sBrkPtInfo.m_bPending = sBrkPtInfoRec.m_bPending; + sBrkPtInfo.m_bCondition = sBrkPtInfoRec.m_bCondition; + sBrkPtInfo.m_strCondition = sBrkPtInfoRec.m_strCondition; + sBrkPtInfo.m_bBrkPtThreadId = sBrkPtInfoRec.m_bBrkPtThreadId; + sBrkPtInfo.m_nBrkPtThreadId = sBrkPtInfoRec.m_nBrkPtThreadId; + + // MI print + // "=breakpoint-modified,bkpt={number=\"%d\",type=\"breakpoint\",disp=\"%s\",enabled=\"%c\",addr=\"0x%016" + // PRIx64 "\", + // func=\"%s\",file=\"%s\",fullname=\"%s/%s\",line=\"%d\",times=\"%d\",original-location=\"%s\"}" + CMICmnMIValueTuple miValueTuple; + if (!rSessionInfo.MIResponseFormBrkPtInfo(sBrkPtInfo, miValueTuple)) { + SetErrorDescription( + CMIUtilString::Format(MIRSRC(IDS_LLDBOUTOFBAND_ERR_FORM_MI_RESPONSE), + "HandleEventSBBreakpointCmn()")); + return MIstatus::failure; + } + + const CMICmnMIValueResult miValueResultC("bkpt", miValueTuple); + const CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_BreakPointModified, miValueResultC); + bool bOk = MiOutOfBandRecordToStdout(miOutOfBandRecord); + bOk = bOk && CMICmnStreamStdout::WritePrompt(); + + return bOk; +} + +//++ +// Details: Handle a LLDB SBBreakpoint added event. +// Add more breakpoint information or overwrite existing information. +// Normally a break point session info objects exists by now when an MI +// command +// was issued to insert a break so the retrieval would normally always +// succeed +// however should a user type "b main" into a console then LLDB will +// create a +// breakpoint directly, hence no MI command, hence no previous record +// of the +// breakpoint so RecordBrkPtInfoGet() will fail. We still get the event +// though +// so need to create a breakpoint info object here and send appropriate +// MI +// response. +// Type: Method. +// Args: vEvent - (R) An LLDB broadcast event. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::HandleEventSBBreakpointAdded( + const lldb::SBEvent &vEvent) { + lldb::SBBreakpoint brkPt = lldb::SBBreakpoint::GetBreakpointFromEvent(vEvent); + if (!brkPt.IsValid()) + return MIstatus::success; + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + CMICmnLLDBDebugSessionInfo::SBrkPtInfo sBrkPtInfo; + if (!rSessionInfo.GetBrkPtInfo(brkPt, sBrkPtInfo)) { + SetErrorDescription( + CMIUtilString::Format(MIRSRC(IDS_LLDBOUTOFBAND_ERR_BRKPT_INFO_GET), + "HandleEventSBBreakpointAdded()", brkPt.GetID())); + return MIstatus::failure; + } + + // CODETAG_LLDB_BREAKPOINT_CREATION + // This is in a worker thread + CMICmnLLDBDebugSessionInfo::SBrkPtInfo sBrkPtInfoRec; + const bool bBrkPtExistAlready = + rSessionInfo.RecordBrkPtInfoGet(brkPt.GetID(), sBrkPtInfoRec); + if (bBrkPtExistAlready) { + // Update breakpoint information object + sBrkPtInfo.m_bDisp = sBrkPtInfoRec.m_bDisp; + sBrkPtInfo.m_bEnabled = brkPt.IsEnabled(); + sBrkPtInfo.m_bHaveArgOptionThreadGrp = false; + sBrkPtInfo.m_strOptThrdGrp.clear(); + sBrkPtInfo.m_nTimes = brkPt.GetHitCount(); + sBrkPtInfo.m_strOrigLoc = sBrkPtInfoRec.m_strOrigLoc; + sBrkPtInfo.m_nIgnore = sBrkPtInfoRec.m_nIgnore; + sBrkPtInfo.m_bPending = sBrkPtInfoRec.m_bPending; + sBrkPtInfo.m_bCondition = sBrkPtInfoRec.m_bCondition; + sBrkPtInfo.m_strCondition = sBrkPtInfoRec.m_strCondition; + sBrkPtInfo.m_bBrkPtThreadId = sBrkPtInfoRec.m_bBrkPtThreadId; + sBrkPtInfo.m_nBrkPtThreadId = sBrkPtInfoRec.m_nBrkPtThreadId; + } else { + // Create a breakpoint information object + sBrkPtInfo.m_bDisp = brkPt.IsOneShot(); + sBrkPtInfo.m_bEnabled = brkPt.IsEnabled(); + sBrkPtInfo.m_bHaveArgOptionThreadGrp = false; + sBrkPtInfo.m_strOptThrdGrp.clear(); + sBrkPtInfo.m_strOrigLoc = CMIUtilString::Format( + "%s:%d", sBrkPtInfo.m_fileName.c_str(), sBrkPtInfo.m_nLine); + sBrkPtInfo.m_nIgnore = brkPt.GetIgnoreCount(); + sBrkPtInfo.m_bPending = false; + const char *pStrCondition = brkPt.GetCondition(); + sBrkPtInfo.m_bCondition = pStrCondition != nullptr; + sBrkPtInfo.m_strCondition = + (pStrCondition != nullptr) ? pStrCondition : "??"; + sBrkPtInfo.m_bBrkPtThreadId = brkPt.GetThreadID() != 0; + sBrkPtInfo.m_nBrkPtThreadId = brkPt.GetThreadID(); + } + + CMICmnMIValueTuple miValueTuple; + if (!rSessionInfo.MIResponseFormBrkPtInfo(sBrkPtInfo, miValueTuple)) { + SetErrorDescription( + CMIUtilString::Format(MIRSRC(IDS_LLDBOUTOFBAND_ERR_FORM_MI_RESPONSE), + "HandleEventSBBreakpointAdded()")); + return MIstatus::failure; + } + + bool bOk = MIstatus::success; + if (bBrkPtExistAlready) { + // MI print + // "=breakpoint-modified,bkpt={number=\"%d\",type=\"breakpoint\",disp=\"%s\",enabled=\"%c\",addr=\"0x%016" + // PRIx64 + // "\",func=\"%s\",file=\"%s\",fullname=\"%s/%s\",line=\"%d\",times=\"%d\",original-location=\"%s\"}" + const CMICmnMIValueResult miValueResult("bkpt", miValueTuple); + const CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_BreakPointModified, miValueResult); + bOk = MiOutOfBandRecordToStdout(miOutOfBandRecord); + bOk = bOk && CMICmnStreamStdout::WritePrompt(); + } else { + // CODETAG_LLDB_BRKPT_ID_MAX + if (brkPt.GetID() > (lldb::break_id_t)rSessionInfo.m_nBrkPointCntMax) { + SetErrorDescription(CMIUtilString::Format( + MIRSRC(IDS_CMD_ERR_BRKPT_CNT_EXCEEDED), + "HandleEventSBBreakpointAdded()", rSessionInfo.m_nBrkPointCntMax, + sBrkPtInfo.m_id)); + return MIstatus::failure; + } + if (!rSessionInfo.RecordBrkPtInfo(brkPt.GetID(), sBrkPtInfo)) { + SetErrorDescription(CMIUtilString::Format( + MIRSRC(IDS_LLDBOUTOFBAND_ERR_BRKPT_INFO_SET), + "HandleEventSBBreakpointAdded()", sBrkPtInfo.m_id)); + return MIstatus::failure; + } + + // MI print + // "=breakpoint-created,bkpt={number=\"%d\",type=\"breakpoint\",disp=\"%s\",enabled=\"%c\",addr=\"0x%016" + // PRIx64 + // "\",func=\"%s\",file=\"%s\",fullname=\"%s/%s\",line=\"%d\",times=\"%d\",original-location=\"%s\"}" + const CMICmnMIValueResult miValueResult("bkpt", miValueTuple); + const CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_BreakPointCreated, miValueResult); + bOk = MiOutOfBandRecordToStdout(miOutOfBandRecord); + bOk = bOk && CMICmnStreamStdout::WritePrompt(); + } + + return bOk; +} + +//++ +// Details: Handle a LLDB SBThread event. +// Type: Method. +// Args: vEvent - (R) An LLDB broadcast event. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::HandleEventSBThread( + const lldb::SBEvent &vEvent) { + if (!ChkForStateChanges()) + return MIstatus::failure; + + bool bOk = MIstatus::success; + const char *pEventType = ""; + const MIuint nEventType = vEvent.GetType(); + switch (nEventType) { + case lldb::SBThread::eBroadcastBitStackChanged: + pEventType = "eBroadcastBitStackChanged"; + bOk = HandleEventSBThreadBitStackChanged(vEvent); + break; + case lldb::SBThread::eBroadcastBitThreadSuspended: + pEventType = "eBroadcastBitThreadSuspended"; + bOk = HandleEventSBThreadSuspended(vEvent); + break; + case lldb::SBThread::eBroadcastBitThreadResumed: + pEventType = "eBroadcastBitThreadResumed"; + break; + case lldb::SBThread::eBroadcastBitSelectedFrameChanged: + pEventType = "eBroadcastBitSelectedFrameChanged"; + break; + case lldb::SBThread::eBroadcastBitThreadSelected: + pEventType = "eBroadcastBitThreadSelected"; + break; + default: { + const CMIUtilString msg( + CMIUtilString::Format(MIRSRC(IDS_LLDBOUTOFBAND_ERR_UNKNOWN_EVENT), + "SBThread", (MIuint)nEventType)); + SetErrorDescription(msg); + return MIstatus::failure; + } + } + m_pLog->WriteLog(CMIUtilString::Format("##### An SBThread event occurred: %s", + pEventType)); + + return bOk; +} + +//++ +// Details: Handle a LLDB SBThread event. +// Type: Method. +// Args: vEvent - (R) An LLDB broadcast event. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::HandleEventSBThreadSuspended( + const lldb::SBEvent &vEvent) { + lldb::SBThread thread = lldb::SBThread::GetThreadFromEvent(vEvent); + if (!thread.IsValid()) + return MIstatus::success; + + const lldb::StopReason eStopReason = thread.GetStopReason(); + if (eStopReason != lldb::eStopReasonSignal) + return MIstatus::success; + + // MI print "@thread=%d,signal=%lld" + const MIuint64 nId = thread.GetStopReasonDataAtIndex(0); + const CMIUtilString strThread( + CMIUtilString::Format("%d", thread.GetThreadID())); + const CMICmnMIValueConst miValueConst(strThread); + const CMICmnMIValueResult miValueResult("thread", miValueConst); + CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_Thread, miValueResult); + const CMIUtilString strSignal(CMIUtilString::Format("%lld", nId)); + const CMICmnMIValueConst miValueConst2(strSignal); + const CMICmnMIValueResult miValueResult2("signal", miValueConst2); + miOutOfBandRecord.Add(miValueResult2); + return MiOutOfBandRecordToStdout(miOutOfBandRecord); +} + +//++ +// Details: Handle a LLDB SBThread event. +// Type: Method. +// Args: vEvent - (R) An LLDB broadcast event. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::HandleEventSBThreadBitStackChanged( + const lldb::SBEvent &vEvent) { + lldb::SBThread thread = lldb::SBThread::GetThreadFromEvent(vEvent); + if (!thread.IsValid()) + return MIstatus::success; + + lldb::SBStream streamOut; + const bool bOk = thread.GetStatus(streamOut); + return bOk && TextToStdout(streamOut.GetData()); +} + +//++ +// Details: Handle a LLDB SBTarget event. +// Type: Method. +// Args: vEvent - (R) An LLDB broadcast event. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::HandleEventSBTarget( + const lldb::SBEvent &vEvent) { + if (!ChkForStateChanges()) + return MIstatus::failure; + + bool bOk = MIstatus::success; + const char *pEventType = ""; + const MIuint nEventType = vEvent.GetType(); + switch (nEventType) { + case lldb::SBTarget::eBroadcastBitBreakpointChanged: + pEventType = "eBroadcastBitBreakpointChanged"; + break; + case lldb::SBTarget::eBroadcastBitModulesLoaded: + pEventType = "eBroadcastBitModulesLoaded"; + bOk = HandleTargetEventBroadcastBitModulesLoaded(vEvent); + break; + case lldb::SBTarget::eBroadcastBitModulesUnloaded: + pEventType = "eBroadcastBitModulesUnloaded"; + bOk = HandleTargetEventBroadcastBitModulesUnloaded(vEvent); + break; + case lldb::SBTarget::eBroadcastBitWatchpointChanged: + pEventType = "eBroadcastBitWatchpointChanged"; + break; + case lldb::SBTarget::eBroadcastBitSymbolsLoaded: + pEventType = "eBroadcastBitSymbolsLoaded"; + break; + default: { + const CMIUtilString msg( + CMIUtilString::Format(MIRSRC(IDS_LLDBOUTOFBAND_ERR_UNKNOWN_EVENT), + "SBTarget", (MIuint)nEventType)); + SetErrorDescription(msg); + return MIstatus::failure; + } + } + m_pLog->WriteLog(CMIUtilString::Format("##### An SBTarget event occurred: %s", + pEventType)); + + return bOk; +} + +//++ +// Details: Print to stdout +// "=library-loaded,id=\"%s\",target-name=\"%s\",host-name=\"%s\",symbols-loaded="%d"[,symbols-path=\"%s\"],loaded_addr=\"0x%016" +// PRIx64"\"" +// Type: Method. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::HandleTargetEventBroadcastBitModulesLoaded( + const lldb::SBEvent &vEvent) { + bool bOk = MIstatus::failure; + const MIuint nSize = lldb::SBTarget::GetNumModulesFromEvent(vEvent); + for (MIuint nIndex = 0; nIndex < nSize; ++nIndex) { + const lldb::SBModule sbModule = + lldb::SBTarget::GetModuleAtIndexFromEvent(nIndex, vEvent); + CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_TargetModuleLoaded); + const bool bWithExtraFields = true; + bOk = MiHelpGetModuleInfo(sbModule, bWithExtraFields, miOutOfBandRecord); + bOk = bOk && MiOutOfBandRecordToStdout(miOutOfBandRecord); + if (!bOk) + break; + } + + return bOk; +} + +//++ +// Details: Print to stdout +// "=library-unloaded,id=\"%s\",target-name=\"%s\",host-name=\"%s\",symbols-loaded="%d"[,symbols-path=\"%s\"],loaded_addr=\"0x%016" +// PRIx64"\"" +// Type: Method. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents:: + HandleTargetEventBroadcastBitModulesUnloaded(const lldb::SBEvent &vEvent) { + bool bOk = MIstatus::failure; + const MIuint nSize = lldb::SBTarget::GetNumModulesFromEvent(vEvent); + for (MIuint nIndex = 0; nIndex < nSize; ++nIndex) { + const lldb::SBModule sbModule = + lldb::SBTarget::GetModuleAtIndexFromEvent(nIndex, vEvent); + CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_TargetModuleUnloaded); + const bool bWithExtraFields = false; + bOk = MiHelpGetModuleInfo(sbModule, bWithExtraFields, miOutOfBandRecord); + bOk = bOk && MiOutOfBandRecordToStdout(miOutOfBandRecord); + if (!bOk) + break; + } + + return bOk; +} + +//++ +// Details: Build module information for =library-loaded/=library-unloaded: +// "id=\"%s\",target-name=\"%s\",host-name=\"%s\",symbols-loaded="%d"[,symbols-path=\"%s\"],loaded_addr=\"0x%016" +// PRIx64"\"" +// Type: Method. +// Args: vwrMiValueList - (W) MI value list object. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::MiHelpGetModuleInfo( + const lldb::SBModule &vModule, const bool vbWithExtraFields, + CMICmnMIOutOfBandRecord &vwrMiOutOfBandRecord) { + bool bOk = MIstatus::success; + + // First, build standard fields: + // Build "id" field + std::unique_ptr apPath(new char[PATH_MAX]); + vModule.GetFileSpec().GetPath(apPath.get(), PATH_MAX); + const CMIUtilString strTargetPath(apPath.get()); + const CMICmnMIValueConst miValueConst(strTargetPath.AddSlashes()); + const CMICmnMIValueResult miValueResult("id", miValueConst); + vwrMiOutOfBandRecord.Add(miValueResult); + // Build "target-name" field + const CMICmnMIValueConst miValueConst2(strTargetPath.AddSlashes()); + const CMICmnMIValueResult miValueResult2("target-name", miValueConst2); + vwrMiOutOfBandRecord.Add(miValueResult2); + // Build "host-name" field + vModule.GetPlatformFileSpec().GetPath(apPath.get(), PATH_MAX); + const CMIUtilString strHostPath(apPath.get()); + const CMICmnMIValueConst miValueConst3(strHostPath.AddSlashes()); + const CMICmnMIValueResult miValueResult3("host-name", miValueConst3); + vwrMiOutOfBandRecord.Add(miValueResult3); + + // Then build extra fields if needed: + if (vbWithExtraFields) { + // Build "symbols-loaded" field + vModule.GetSymbolFileSpec().GetPath(apPath.get(), PATH_MAX); + const CMIUtilString strSymbolsPath(apPath.get()); + const bool bSymbolsLoaded = + !CMIUtilString::Compare(strHostPath, strSymbolsPath); + const CMICmnMIValueConst miValueConst4( + CMIUtilString::Format("%d", bSymbolsLoaded)); + const CMICmnMIValueResult miValueResult4("symbols-loaded", miValueConst4); + vwrMiOutOfBandRecord.Add(miValueResult4); + // Build "symbols-path" field + if (bSymbolsLoaded) { + const CMICmnMIValueConst miValueConst5(strSymbolsPath.AddSlashes()); + const CMICmnMIValueResult miValueResult5("symbols-path", miValueConst5); + vwrMiOutOfBandRecord.Add(miValueResult5); + } + // Build "loaded_addr" field + lldb::SBAddress sbAddress(vModule.GetObjectFileHeaderAddress()); + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + const lldb::addr_t nLoadAddress( + sbAddress.GetLoadAddress(rSessionInfo.GetTarget())); + const CMIUtilString strLoadedAddr( + nLoadAddress != LLDB_INVALID_ADDRESS + ? CMIUtilString::Format("0x%016" PRIx64, nLoadAddress) + : "-"); + const CMICmnMIValueConst miValueConst6(strLoadedAddr); + const CMICmnMIValueResult miValueResult6("loaded_addr", miValueConst6); + vwrMiOutOfBandRecord.Add(miValueResult6); + + // Build "size" field + lldb::SBSection sbSection = sbAddress.GetSection(); + const CMIUtilString strSize( + CMIUtilString::Format("%" PRIu64, sbSection.GetByteSize())); + const CMICmnMIValueConst miValueConst7(strSize); + const CMICmnMIValueResult miValueResult7("size", miValueConst7); + vwrMiOutOfBandRecord.Add(miValueResult7); + } + + return bOk; +} + +//++ +// Details: Handle a LLDB SBCommandInterpreter event. +// Type: Method. +// Args: vEvent - (R) An LLDB command interpreter event. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::HandleEventSBCommandInterpreter( + const lldb::SBEvent &vEvent) { + // This function is not used + // *** This function is under development + + const char *pEventType = ""; + const MIuint nEventType = vEvent.GetType(); + switch (nEventType) { + case lldb::SBCommandInterpreter::eBroadcastBitThreadShouldExit: + pEventType = "eBroadcastBitThreadShouldExit"; + // ToDo: IOR: Reminder to maybe handle this here + // const MIuint nEventType = event.GetType(); + // if (nEventType & + // lldb::SBCommandInterpreter::eBroadcastBitThreadShouldExit) + //{ + // m_pClientDriver->SetExitApplicationFlag(); + // vrbYesExit = true; + // return MIstatus::success; + //} + break; + case lldb::SBCommandInterpreter::eBroadcastBitResetPrompt: + pEventType = "eBroadcastBitResetPrompt"; + break; + case lldb::SBCommandInterpreter::eBroadcastBitQuitCommandReceived: { + pEventType = "eBroadcastBitQuitCommandReceived"; + const bool bForceExit = true; + CMICmnLLDBDebugger::Instance().GetDriver().SetExitApplicationFlag( + bForceExit); + break; + } + case lldb::SBCommandInterpreter::eBroadcastBitAsynchronousOutputData: + pEventType = "eBroadcastBitAsynchronousOutputData"; + break; + case lldb::SBCommandInterpreter::eBroadcastBitAsynchronousErrorData: + pEventType = "eBroadcastBitAsynchronousErrorData"; + break; + default: { + const CMIUtilString msg( + CMIUtilString::Format(MIRSRC(IDS_LLDBOUTOFBAND_ERR_UNKNOWN_EVENT), + "SBCommandInterpreter", (MIuint)nEventType)); + SetErrorDescription(msg); + return MIstatus::failure; + } + } + m_pLog->WriteLog(CMIUtilString::Format( + "##### An SBCommandInterpreter event occurred: %s", pEventType)); + + return MIstatus::success; +} + +//++ +// Details: Handle SBProcess event eBroadcastBitStateChanged. +// Type: Method. +// Args: vEvent - (R) An LLDB event object. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::HandleProcessEventBroadcastBitStateChanged( + const lldb::SBEvent &vEvent) { + // Make sure the program hasn't been auto-restarted: + if (lldb::SBProcess::GetRestartedFromEvent(vEvent)) + return MIstatus::success; + + bool bOk = ChkForStateChanges(); + bOk = bOk && GetProcessStdout(); + bOk = bOk && GetProcessStderr(); + if (!bOk) + return MIstatus::failure; + + // Something changed in the process; get the event and report the process's + // current + // status and location + const lldb::StateType eEventState = + lldb::SBProcess::GetStateFromEvent(vEvent); + if (eEventState == lldb::eStateInvalid) + return MIstatus::success; + + lldb::SBProcess process = lldb::SBProcess::GetProcessFromEvent(vEvent); + if (!process.IsValid()) { + const CMIUtilString msg(CMIUtilString::Format( + MIRSRC(IDS_LLDBOUTOFBAND_ERR_PROCESS_INVALID), "SBProcess", + "HandleProcessEventBroadcastBitStateChanged()")); + SetErrorDescription(msg); + return MIstatus::failure; + } + + bool bShouldBrk = true; + const char *pEventType = ""; + switch (eEventState) { + case lldb::eStateUnloaded: + pEventType = "eStateUnloaded"; + break; + case lldb::eStateConnected: + pEventType = "eStateConnected"; + break; + case lldb::eStateAttaching: + pEventType = "eStateAttaching"; + break; + case lldb::eStateLaunching: + pEventType = "eStateLaunching"; + break; + case lldb::eStateStopped: + pEventType = "eStateStopped"; + bOk = HandleProcessEventStateStopped(vEvent, bShouldBrk); + if (bShouldBrk) + break; + LLVM_FALLTHROUGH; + case lldb::eStateCrashed: + case lldb::eStateSuspended: + pEventType = "eStateSuspended"; + bOk = HandleProcessEventStateSuspended(vEvent); + break; + case lldb::eStateRunning: + pEventType = "eStateRunning"; + bOk = HandleProcessEventStateRunning(); + break; + case lldb::eStateStepping: + pEventType = "eStateStepping"; + break; + case lldb::eStateDetached: + pEventType = "eStateDetached"; + break; + case lldb::eStateExited: + // Don't exit from lldb-mi here. We should be able to re-run target. + pEventType = "eStateExited"; + bOk = HandleProcessEventStateExited(); + break; + default: { + const CMIUtilString msg(CMIUtilString::Format( + MIRSRC(IDS_LLDBOUTOFBAND_ERR_UNKNOWN_EVENT), + "SBProcess BroadcastBitStateChanged", (MIuint)eEventState)); + SetErrorDescription(msg); + return MIstatus::failure; + } + } + + // ToDo: Remove when finished coding application + m_pLog->WriteLog(CMIUtilString::Format( + "##### An SB Process event BroadcastBitStateChanged occurred: %s", + pEventType)); + + return bOk; +} + +//++ +// Details: Asynchronous event handler for LLDB Process state suspended. +// Type: Method. +// Args: vEvent - (R) An LLDB event object. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::HandleProcessEventStateSuspended( + const lldb::SBEvent &vEvent) { + bool bOk = MIstatus::success; + lldb::SBStream streamOut; + lldb::SBDebugger &rDebugger = + CMICmnLLDBDebugSessionInfo::Instance().GetDebugger(); + lldb::SBProcess sbProcess = + CMICmnLLDBDebugSessionInfo::Instance().GetProcess(); + lldb::SBTarget target = sbProcess.GetTarget(); + if (rDebugger.GetSelectedTarget() == target) { + if (!UpdateSelectedThread()) + return MIstatus::failure; + sbProcess.GetDescription(streamOut); + // Add a delimiter between process' and threads' info. + streamOut.Printf("\n"); + for (uint32_t i = 0, e = sbProcess.GetNumThreads(); i < e; ++i) { + const lldb::SBThread thread = sbProcess.GetThreadAtIndex(i); + if (!thread.IsValid()) + continue; + thread.GetDescription(streamOut); + } + bOk = TextToStdout(streamOut.GetData()); + } else { + const MIuint nTargetIndex = rDebugger.GetIndexOfTarget(target); + if (nTargetIndex != UINT_MAX) + streamOut.Printf("Target %" PRIu64 ": (", (uint64_t)nTargetIndex); + else + streamOut.Printf("Target : ("); + target.GetDescription(streamOut, lldb::eDescriptionLevelBrief); + streamOut.Printf(") stopped.\n"); + bOk = TextToStdout(streamOut.GetData()); + } + + return bOk; +} + +//++ +// Details: Print to stdout MI formatted text to indicate process stopped. +// Type: Method. +// Args: vwrbShouldBrk - (W) True = Yes break, false = do not. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::HandleProcessEventStateStopped( + const lldb::SBEvent &vrEvent, bool &vwrbShouldBrk) { + if (!UpdateSelectedThread()) + return MIstatus::failure; + + const char *pEventType = ""; + bool bOk = MIstatus::success; + lldb::SBProcess sbProcess = + CMICmnLLDBDebugSessionInfo::Instance().GetProcess(); + const lldb::StopReason eStoppedReason = + sbProcess.GetSelectedThread().GetStopReason(); + switch (eStoppedReason) { + case lldb::eStopReasonInvalid: + pEventType = "eStopReasonInvalid"; + vwrbShouldBrk = false; + break; + case lldb::eStopReasonNone: + pEventType = "eStopReasonNone"; + break; + case lldb::eStopReasonTrace: + pEventType = "eStopReasonTrace"; + bOk = HandleProcessEventStopReasonTrace(); + break; + case lldb::eStopReasonBreakpoint: + pEventType = "eStopReasonBreakpoint"; + bOk = HandleProcessEventStopReasonBreakpoint(); + break; + case lldb::eStopReasonWatchpoint: + pEventType = "eStopReasonWatchpoint"; + bOk = HandleProcessEventStopReasonWatchpoint(); + break; + case lldb::eStopReasonSignal: + pEventType = "eStopReasonSignal"; + bOk = HandleProcessEventStopSignal(vrEvent); + break; + case lldb::eStopReasonException: + pEventType = "eStopReasonException"; + bOk = HandleProcessEventStopException(); + break; + case lldb::eStopReasonExec: + pEventType = "eStopReasonExec"; + break; + case lldb::eStopReasonPlanComplete: + pEventType = "eStopReasonPlanComplete"; + bOk = HandleProcessEventStopReasonTrace(); + break; + case lldb::eStopReasonThreadExiting: + pEventType = "eStopReasonThreadExiting"; + break; + case lldb::eStopReasonInstrumentation: + pEventType = "eStopReasonInstrumentation"; + break; + } + + // ToDo: Remove when finished coding application + m_pLog->WriteLog(CMIUtilString::Format( + "##### An SB Process event stop state occurred: %s", pEventType)); + + return bOk; +} + +//++ +// Details: Asynchronous event handler for LLDB Process stop signal. +// Type: Method. +// Args: vrEvent - (R) An LLDB broadcast event. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::HandleProcessEventStopSignal( + const lldb::SBEvent &vrEvent) { + bool bOk = MIstatus::success; + + InitializeSignals(); + lldb::SBProcess sbProcess = + CMICmnLLDBDebugSessionInfo::Instance().GetProcess(); + const MIuint64 nStopReason = + sbProcess.GetSelectedThread().GetStopReasonDataAtIndex(0); + const bool bInterrupted = lldb::SBProcess::GetInterruptedFromEvent(vrEvent); + if (nStopReason == m_SIGINT || (nStopReason == m_SIGSTOP && bInterrupted)) { + // MI print + // "*stopped,reason=\"signal-received\",signal-name=\"SIGINT\",signal-meaning=\"Interrupt\",frame={%s},thread-id=\"%d\",stopped-threads=\"all\"" + const CMICmnMIValueConst miValueConst("signal-received"); + const CMICmnMIValueResult miValueResult("reason", miValueConst); + CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_Stopped, miValueResult); + const CMICmnMIValueConst miValueConst2("SIGINT"); + const CMICmnMIValueResult miValueResult2("signal-name", miValueConst2); + miOutOfBandRecord.Add(miValueResult2); + const CMICmnMIValueConst miValueConst3("Interrupt"); + const CMICmnMIValueResult miValueResult3("signal-meaning", miValueConst3); + miOutOfBandRecord.Add(miValueResult3); + CMICmnMIValueTuple miValueTuple; + bOk = bOk && MiHelpGetCurrentThreadFrame(miValueTuple); + const CMICmnMIValueResult miValueResult4("frame", miValueTuple); + miOutOfBandRecord.Add(miValueResult4); + const CMIUtilString strThreadId(CMIUtilString::Format( + "%" PRIu32, sbProcess.GetSelectedThread().GetIndexID())); + const CMICmnMIValueConst miValueConst5(strThreadId); + const CMICmnMIValueResult miValueResult5("thread-id", miValueConst5); + miOutOfBandRecord.Add(miValueResult5); + const CMICmnMIValueConst miValueConst6("all"); + const CMICmnMIValueResult miValueResult6("stopped-threads", miValueConst6); + miOutOfBandRecord.Add(miValueResult6); + bOk = bOk && MiOutOfBandRecordToStdout(miOutOfBandRecord); + bOk = bOk && CMICmnStreamStdout::WritePrompt(); + } else if (nStopReason == m_SIGSTOP) { + // MI print + // "*stopped,reason=\"signal-received\",signal-name=\"SIGSTOP\",signal-meaning=\"Stop\",frame={%s},thread-id=\"%d\",stopped-threads=\"all\"" + const CMICmnMIValueConst miValueConst("signal-received"); + const CMICmnMIValueResult miValueResult("reason", miValueConst); + CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_Stopped, miValueResult); + const CMICmnMIValueConst miValueConst2("SIGSTOP"); + const CMICmnMIValueResult miValueResult2("signal-name", miValueConst2); + miOutOfBandRecord.Add(miValueResult2); + const CMICmnMIValueConst miValueConst3("Stop"); + const CMICmnMIValueResult miValueResult3("signal-meaning", miValueConst3); + miOutOfBandRecord.Add(miValueResult3); + CMICmnMIValueTuple miValueTuple; + bOk = bOk && MiHelpGetCurrentThreadFrame(miValueTuple); + const CMICmnMIValueResult miValueResult4("frame", miValueTuple); + miOutOfBandRecord.Add(miValueResult4); + const CMIUtilString strThreadId(CMIUtilString::Format( + "%" PRIu32, sbProcess.GetSelectedThread().GetIndexID())); + const CMICmnMIValueConst miValueConst5(strThreadId); + const CMICmnMIValueResult miValueResult5("thread-id", miValueConst5); + miOutOfBandRecord.Add(miValueResult5); + const CMICmnMIValueConst miValueConst6("all"); + const CMICmnMIValueResult miValueResult6("stopped-threads", miValueConst6); + miOutOfBandRecord.Add(miValueResult6); + bOk = bOk && MiOutOfBandRecordToStdout(miOutOfBandRecord); + bOk = bOk && CMICmnStreamStdout::WritePrompt(); + } else if (nStopReason == m_SIGSEGV) { + // MI print + // "*stopped,reason=\"signal-received\",signal-name=\"SIGSEGV\",signal-meaning=\"Segmentation + // fault\",thread-id=\"%d\",frame={%s}" + const CMICmnMIValueConst miValueConst("signal-received"); + const CMICmnMIValueResult miValueResult("reason", miValueConst); + CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_Stopped, miValueResult); + const CMICmnMIValueConst miValueConst2("SIGSEGV"); + const CMICmnMIValueResult miValueResult2("signal-name", miValueConst2); + miOutOfBandRecord.Add(miValueResult2); + const CMICmnMIValueConst miValueConst3("Segmentation fault"); + const CMICmnMIValueResult miValueResult3("signal-meaning", miValueConst3); + miOutOfBandRecord.Add(miValueResult3); + CMICmnMIValueTuple miValueTuple; + bOk = bOk && MiHelpGetCurrentThreadFrame(miValueTuple); + const CMICmnMIValueResult miValueResult4("frame", miValueTuple); + miOutOfBandRecord.Add(miValueResult4); + const CMIUtilString strThreadId(CMIUtilString::Format( + "%d", sbProcess.GetSelectedThread().GetIndexID())); + const CMICmnMIValueConst miValueConst5(strThreadId); + const CMICmnMIValueResult miValueResult5("thread-id", miValueConst5); + miOutOfBandRecord.Add(miValueResult5); + bOk = bOk && MiOutOfBandRecordToStdout(miOutOfBandRecord); + // Note no "(gdb)" output here + } else if (nStopReason == m_SIGTRAP) { + lldb::SBThread thread = sbProcess.GetSelectedThread(); + const MIuint nFrames = thread.GetNumFrames(); + if (nFrames > 0) { + lldb::SBFrame frame = thread.GetFrameAtIndex(0); + const char *pFnName = frame.GetFunctionName(); + if (pFnName != nullptr) { + const CMIUtilString fnName = CMIUtilString(pFnName); + static const CMIUtilString threadCloneFn = + CMIUtilString("__pthread_clone"); + + if (CMIUtilString::Compare(threadCloneFn, fnName)) { + if (sbProcess.IsValid()) + sbProcess.Continue(); + } + } + } + } else { + // MI print + // "*stopped,reason=\"signal-received\",signal-name=\"%s\",thread-id=\"%d\",stopped-threads=\"all\"" + // MI print + // "*stopped,reason=\"signal-received\",signal=\"%d\",thread-id=\"%d\",stopped-threads=\"all\"" + const CMICmnMIValueConst miValueConst("signal-received"); + const CMICmnMIValueResult miValueResult("reason", miValueConst); + CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_Stopped, miValueResult); + lldb::SBUnixSignals sbUnixSignals = sbProcess.GetUnixSignals(); + const char *pSignal = sbUnixSignals.GetSignalAsCString(nStopReason); + if (pSignal) { + const CMICmnMIValueConst miValueConst2(pSignal); + const CMICmnMIValueResult miValueResult2("signal-name", miValueConst2); + miOutOfBandRecord.Add(miValueResult2); + } else { + const CMIUtilString strSignal( + CMIUtilString::Format("%" PRIu64, nStopReason)); + const CMICmnMIValueConst miValueConst2(strSignal); + const CMICmnMIValueResult miValueResult2("signal", miValueConst2); + miOutOfBandRecord.Add(miValueResult2); + } + const CMIUtilString strThreadId(CMIUtilString::Format( + "%d", sbProcess.GetSelectedThread().GetIndexID())); + const CMICmnMIValueConst miValueConst3(strThreadId); + const CMICmnMIValueResult miValueResult3("thread-id", miValueConst3); + miOutOfBandRecord.Add(miValueResult3); + const CMICmnMIValueConst miValueConst4("all"); + const CMICmnMIValueResult miValueResult4("stopped-threads", miValueConst4); + miOutOfBandRecord.Add(miValueResult4); + bOk = bOk && MiOutOfBandRecordToStdout(miOutOfBandRecord); + bOk = bOk && CMICmnStreamStdout::WritePrompt(); + } + return bOk; +} + +//++ +// Details: Asynchronous event handler for LLDB Process stop exception. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::HandleProcessEventStopException() { + const lldb::SBProcess sbProcess = + CMICmnLLDBDebugSessionInfo::Instance().GetProcess(); + lldb::SBThread sbThread = sbProcess.GetSelectedThread(); + const size_t nStopDescriptionLen = sbThread.GetStopDescription(nullptr, 0); + std::unique_ptr apStopDescription(new char[nStopDescriptionLen]); + sbThread.GetStopDescription(apStopDescription.get(), nStopDescriptionLen); + + // MI print + // "*stopped,reason=\"exception-received\",exception=\"%s\",thread-id=\"%d\",stopped-threads=\"all\"" + const CMICmnMIValueConst miValueConst("exception-received"); + const CMICmnMIValueResult miValueResult("reason", miValueConst); + CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_Stopped, miValueResult); + const CMIUtilString strReason(apStopDescription.get()); + const CMICmnMIValueConst miValueConst2(strReason); + const CMICmnMIValueResult miValueResult2("exception", miValueConst2); + miOutOfBandRecord.Add(miValueResult2); + const CMIUtilString strThreadId( + CMIUtilString::Format("%d", sbThread.GetIndexID())); + const CMICmnMIValueConst miValueConst3(strThreadId); + const CMICmnMIValueResult miValueResult3("thread-id", miValueConst3); + miOutOfBandRecord.Add(miValueResult3); + const CMICmnMIValueConst miValueConst4("all"); + const CMICmnMIValueResult miValueResult4("stopped-threads", miValueConst4); + miOutOfBandRecord.Add(miValueResult4); + bool bOk = MiOutOfBandRecordToStdout(miOutOfBandRecord); + bOk = bOk && CMICmnStreamStdout::WritePrompt(); + + return bOk; +} + +//++ +// Details: Form partial MI response in a MI value tuple object. +// Type: Method. +// Args: vwrMiValueTuple - (W) MI value tuple object. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::MiHelpGetCurrentThreadFrame( + CMICmnMIValueTuple &vwrMiValueTuple) { + CMIUtilString strThreadFrame; + lldb::SBProcess sbProcess = + CMICmnLLDBDebugSessionInfo::Instance().GetProcess(); + lldb::SBThread thread = sbProcess.GetSelectedThread(); + const MIuint nFrame = thread.GetNumFrames(); + if (nFrame == 0) { + // MI print + // "addr=\"??\",func=\"??\",file=\"??\",fullname=\"??\",line=\"??\"" + const CMICmnMIValueConst miValueConst("??"); + const CMICmnMIValueResult miValueResult("addr", miValueConst); + CMICmnMIValueTuple miValueTuple(miValueResult); + const CMICmnMIValueResult miValueResult2("func", miValueConst); + miValueTuple.Add(miValueResult2); + const CMICmnMIValueResult miValueResult4("file", miValueConst); + miValueTuple.Add(miValueResult4); + const CMICmnMIValueResult miValueResult5("fullname", miValueConst); + miValueTuple.Add(miValueResult5); + const CMICmnMIValueResult miValueResult6("line", miValueConst); + miValueTuple.Add(miValueResult6); + + vwrMiValueTuple = miValueTuple; + + return MIstatus::success; + } + + CMICmnMIValueTuple miValueTuple; + if (!CMICmnLLDBDebugSessionInfo::Instance().MIResponseFormFrameInfo( + thread, 0, CMICmnLLDBDebugSessionInfo::eFrameInfoFormat_NoArguments, + miValueTuple)) { + SetErrorDescription( + CMIUtilString::Format(MIRSRC(IDS_LLDBOUTOFBAND_ERR_FORM_MI_RESPONSE), + "MiHelpGetCurrentThreadFrame()")); + return MIstatus::failure; + } + + vwrMiValueTuple = miValueTuple; + + return MIstatus::success; +} + +//++ +// Details: Asynchronous event handler for LLDB Process stop reason breakpoint. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::HandleProcessEventStopReasonBreakpoint() { + // CODETAG_DEBUG_SESSION_RUNNING_PROG_RECEIVED_SIGINT_PAUSE_PROGRAM + if (!CMIDriver::Instance().SetDriverStateRunningNotDebugging()) { + const CMIUtilString &rErrMsg(CMIDriver::Instance().GetErrorDescription()); + SetErrorDescription(CMIUtilString::Format( + MIRSRC(IDS_LLDBOUTOFBAND_ERR_SETNEWDRIVERSTATE), + "HandleProcessEventStopReasonBreakpoint()", rErrMsg.c_str())); + return MIstatus::failure; + } + + lldb::SBProcess sbProcess = + CMICmnLLDBDebugSessionInfo::Instance().GetProcess(); + const MIuint64 brkPtId = + sbProcess.GetSelectedThread().GetStopReasonDataAtIndex(0); + lldb::SBBreakpoint brkPt = + CMICmnLLDBDebugSessionInfo::Instance().GetTarget().GetBreakpointAtIndex( + (MIuint)brkPtId); + + return MiStoppedAtBreakPoint(brkPtId, brkPt); +} + +//++ +// Details: Form the MI Out-of-band response for stopped reason on hitting a +// break point. +// Type: Method. +// Args: vBrkPtId - (R) The LLDB break point's ID +// vBrkPt - (R) THe LLDB break point object. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::MiStoppedAtBreakPoint( + const MIuint64 vBrkPtId, const lldb::SBBreakpoint &vBrkPt) { + bool bOk = MIstatus::success; + + lldb::SBProcess sbProcess = + CMICmnLLDBDebugSessionInfo::Instance().GetProcess(); + lldb::SBThread thread = sbProcess.GetSelectedThread(); + const MIuint nFrame = thread.GetNumFrames(); + if (nFrame == 0) { + // MI print + // "*stopped,reason=\"breakpoint-hit\",disp=\"del\",bkptno=\"%d\",frame={},thread-id=\"%d\",stopped-threads=\"all\"" + const CMICmnMIValueConst miValueConst("breakpoint-hit"); + const CMICmnMIValueResult miValueResult("reason", miValueConst); + CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_Stopped, miValueResult); + const CMICmnMIValueConst miValueConst2("del"); + const CMICmnMIValueResult miValueResult2("disp", miValueConst2); + miOutOfBandRecord.Add(miValueResult2); + const CMIUtilString strBkp(CMIUtilString::Format("%d", vBrkPtId)); + const CMICmnMIValueConst miValueConst3(strBkp); + CMICmnMIValueResult miValueResult3("bkptno", miValueConst3); + miOutOfBandRecord.Add(miValueResult3); + const CMICmnMIValueConst miValueConst4("{}"); + const CMICmnMIValueResult miValueResult4("frame", miValueConst4); + miOutOfBandRecord.Add(miValueResult4); + const CMIUtilString strThreadId( + CMIUtilString::Format("%d", vBrkPt.GetThreadIndex())); + const CMICmnMIValueConst miValueConst5(strThreadId); + const CMICmnMIValueResult miValueResult5("thread-id", miValueConst5); + miOutOfBandRecord.Add(miValueResult5); + const CMICmnMIValueConst miValueConst6("all"); + const CMICmnMIValueResult miValueResult6("stopped-threads", miValueConst6); + miOutOfBandRecord.Add(miValueResult6); + bOk = bOk && MiOutOfBandRecordToStdout(miOutOfBandRecord); + bOk = bOk && CMICmnStreamStdout::WritePrompt(); + return bOk; + } + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + + // MI print + // "*stopped,reason=\"breakpoint-hit\",disp=\"del\",bkptno=\"%d\",frame={addr=\"0x%016" + // PRIx64 + // "\",func=\"%s\",args=[],file=\"%s\",fullname=\"%s\",line=\"%d\"},thread-id=\"%d\",stopped-threads=\"all\"" + const CMICmnMIValueConst miValueConst("breakpoint-hit"); + const CMICmnMIValueResult miValueResult("reason", miValueConst); + CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_Stopped, miValueResult); + const CMICmnMIValueConst miValueConstA("del"); + const CMICmnMIValueResult miValueResultA("disp", miValueConstA); + miOutOfBandRecord.Add(miValueResultA); + const CMIUtilString strBkp(CMIUtilString::Format("%d", vBrkPtId)); + const CMICmnMIValueConst miValueConstB(strBkp); + CMICmnMIValueResult miValueResultB("bkptno", miValueConstB); + miOutOfBandRecord.Add(miValueResultB); + + // frame={addr=\"0x%016" PRIx64 + // "\",func=\"%s\",args=[],file=\"%s\",fullname=\"%s\",line=\"%d\"} + if (bOk) { + CMICmnMIValueTuple miValueTuple; + bOk = bOk && + rSessionInfo.MIResponseFormFrameInfo( + thread, 0, + CMICmnLLDBDebugSessionInfo::eFrameInfoFormat_AllArguments, + miValueTuple); + const CMICmnMIValueResult miValueResult8("frame", miValueTuple); + miOutOfBandRecord.Add(miValueResult8); + } + + // Add to MI thread-id=\"%d\",stopped-threads=\"all\" + if (bOk) { + const CMIUtilString strThreadId( + CMIUtilString::Format("%d", thread.GetIndexID())); + const CMICmnMIValueConst miValueConst8(strThreadId); + const CMICmnMIValueResult miValueResult8("thread-id", miValueConst8); + miOutOfBandRecord.Add(miValueResult8); + } + if (bOk) { + const CMICmnMIValueConst miValueConst9("all"); + const CMICmnMIValueResult miValueResult9("stopped-threads", miValueConst9); + miOutOfBandRecord.Add(miValueResult9); + bOk = MiOutOfBandRecordToStdout(miOutOfBandRecord); + bOk = bOk && CMICmnStreamStdout::WritePrompt(); + } + + return MIstatus::success; +} + +//++ +//------------------------------------------------------------------------------------ +// Details: Asynchronous event handler for LLDB Process stop reason watchpoint. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::HandleProcessEventStopReasonWatchpoint() { + // CODETAG_DEBUG_SESSION_RUNNING_PROG_RECEIVED_SIGINT_PAUSE_PROGRAM + if (!CMIDriver::Instance().SetDriverStateRunningNotDebugging()) { + const CMIUtilString &rErrMsg(CMIDriver::Instance().GetErrorDescription()); + SetErrorDescription(CMIUtilString::Format( + MIRSRC(IDS_LLDBOUTOFBAND_ERR_SETNEWDRIVERSTATE), + "HandleProcessEventStopReasonWatchpoint()", rErrMsg.c_str())); + return MIstatus::failure; + } + + lldb::SBProcess sbProcess = + CMICmnLLDBDebugSessionInfo::Instance().GetProcess(); + const MIuint64 brkPtId = + sbProcess.GetSelectedThread().GetStopReasonDataAtIndex(0); + lldb::SBBreakpoint brkPt = + CMICmnLLDBDebugSessionInfo::Instance().GetTarget().GetBreakpointAtIndex( + (MIuint)brkPtId); + + return MiStoppedAtWatchPoint(brkPtId, brkPt); +} + +//++ +//------------------------------------------------------------------------------------ +// Details: Form the MI Out-of-band response for stopped reason on hitting a +// break point. +// Type: Method. +// Args: vBrkPtId - (R) The LLDB break point's ID +// vBrkPt - (R) THe LLDB break point object. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::MiStoppedAtWatchPoint( + const MIuint64 vBrkPtId, const lldb::SBBreakpoint &vBrkPt) { + bool bOk = MIstatus::success; + + lldb::SBProcess sbProcess = + CMICmnLLDBDebugSessionInfo::Instance().GetProcess(); + lldb::SBThread thread = sbProcess.GetSelectedThread(); + const MIuint nFrame = thread.GetNumFrames(); + if (nFrame == 0) { + // MI print + // *stopped,reason="breakpoint-hit",disp="del",bkptno="1",frame={level="0",addr="0x00000000ed2b8f00",func="void art::interpreter::ExecuteSwitchImplCpp(art::interpreter::SwitchImplContext*)",args=[],file="??",fullname="??",line="-1"},thread-id="1",stopped-threads="all" + // "*stopped,reason=\"breakpoint-hit\",disp=\"del\",bkptno=\"%d\",frame={},thread-id=\"%d\",stopped-threads=\"all\"" + const CMICmnMIValueConst miValueConst("watchpoint-hit"); + const CMICmnMIValueResult miValueResult("reason", miValueConst); + CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_Stopped, miValueResult); + const CMICmnMIValueConst miValueConst2("del"); + const CMICmnMIValueResult miValueResult2("disp", miValueConst2); + miOutOfBandRecord.Add(miValueResult2); + const CMIUtilString strBkp(CMIUtilString::Format("%d", vBrkPtId)); + const CMICmnMIValueConst miValueConst3(strBkp); + CMICmnMIValueResult miValueResult3("bkptno", miValueConst3); + miOutOfBandRecord.Add(miValueResult3); + const CMICmnMIValueConst miValueConst4("{}"); + const CMICmnMIValueResult miValueResult4("frame", miValueConst4); + miOutOfBandRecord.Add(miValueResult4); + const CMIUtilString strThreadId( + CMIUtilString::Format("%d", vBrkPt.GetThreadIndex())); + const CMICmnMIValueConst miValueConst5(strThreadId); + const CMICmnMIValueResult miValueResult5("thread-id", miValueConst5); + miOutOfBandRecord.Add(miValueResult5); + const CMICmnMIValueConst miValueConst6("all"); + const CMICmnMIValueResult miValueResult6("stopped-threads", miValueConst6); + miOutOfBandRecord.Add(miValueResult6); + bOk = bOk && MiOutOfBandRecordToStdout(miOutOfBandRecord); + bOk = bOk && CMICmnStreamStdout::WritePrompt(); + return bOk; + } + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + + // MI print + // "*stopped,reason=\"breakpoint-hit\",disp=\"del\",bkptno=\"%d\",frame={addr=\"0x%016" + // PRIx64 + // "\",func=\"%s\",args=[],file=\"%s\",fullname=\"%s\",line=\"%d\"},thread-id=\"%d\",stopped-threads=\"all\"" + const CMICmnMIValueConst miValueConst("watchpoint-hit"); + const CMICmnMIValueResult miValueResult("reason", miValueConst); + CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_Stopped, miValueResult); + const CMICmnMIValueConst miValueConstA("del"); + const CMICmnMIValueResult miValueResultA("disp", miValueConstA); + miOutOfBandRecord.Add(miValueResultA); + const CMIUtilString strBkp(CMIUtilString::Format("%d", vBrkPtId)); + const CMICmnMIValueConst miValueConstB(strBkp); + CMICmnMIValueResult miValueResultB("bkptno", miValueConstB); + miOutOfBandRecord.Add(miValueResultB); + + // frame={addr=\"0x%016" PRIx64 + // "\",func=\"%s\",args=[],file=\"%s\",fullname=\"%s\",line=\"%d\"} + if (bOk) { + CMICmnMIValueTuple miValueTuple; + bOk = bOk && + rSessionInfo.MIResponseFormFrameInfo( + thread, 0, + CMICmnLLDBDebugSessionInfo::eFrameInfoFormat_AllArguments, + miValueTuple); + const CMICmnMIValueResult miValueResult8("frame", miValueTuple); + miOutOfBandRecord.Add(miValueResult8); + } + + // Add to MI thread-id=\"%d\",stopped-threads=\"all\" + if (bOk) { + const CMIUtilString strThreadId( + CMIUtilString::Format("%d", thread.GetIndexID())); + const CMICmnMIValueConst miValueConst8(strThreadId); + const CMICmnMIValueResult miValueResult8("thread-id", miValueConst8); + miOutOfBandRecord.Add(miValueResult8); + } + if (bOk) { + const CMICmnMIValueConst miValueConst9("all"); + const CMICmnMIValueResult miValueResult9("stopped-threads", miValueConst9); + miOutOfBandRecord.Add(miValueResult9); + bOk = MiOutOfBandRecordToStdout(miOutOfBandRecord); + bOk = bOk && CMICmnStreamStdout::WritePrompt(); + } + + return MIstatus::success; +} + +//++ +// Details: Asynchronous event handler for LLDB Process stop reason trace. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::HandleProcessEventStopReasonTrace() { + bool bOk = true; + lldb::SBProcess sbProcess = + CMICmnLLDBDebugSessionInfo::Instance().GetProcess(); + lldb::SBThread thread = sbProcess.GetSelectedThread(); + const MIuint nFrame = thread.GetNumFrames(); + if (nFrame == 0) { + // MI print "*stopped,reason=\"trace\",stopped-threads=\"all\"" + const CMICmnMIValueConst miValueConst("trace"); + const CMICmnMIValueResult miValueResult("reason", miValueConst); + CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_Stopped, miValueResult); + const CMICmnMIValueConst miValueConst2("all"); + const CMICmnMIValueResult miValueResult2("stopped-threads", miValueConst2); + miOutOfBandRecord.Add(miValueResult2); + bOk = MiOutOfBandRecordToStdout(miOutOfBandRecord); + bOk = bOk && CMICmnStreamStdout::WritePrompt(); + return bOk; + } + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + + // MI print + // "*stopped,reason=\"end-stepping-range\",frame={addr=\"0x%016" PRIx64 + // "\",func=\"%s\",args=[\"%s\"],file=\"%s\",fullname=\"%s\",line=\"%d\"},thread-id=\"%d\",stopped-threads=\"all\"" + + // Function args + CMICmnMIValueTuple miValueTuple; + if (!rSessionInfo.MIResponseFormFrameInfo( + thread, 0, CMICmnLLDBDebugSessionInfo::eFrameInfoFormat_AllArguments, + miValueTuple)) + return MIstatus::failure; + + const CMICmnMIValueConst miValueConst("end-stepping-range"); + const CMICmnMIValueResult miValueResult("reason", miValueConst); + CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_Stopped, miValueResult); + const CMICmnMIValueResult miValueResult2("frame", miValueTuple); + miOutOfBandRecord.Add(miValueResult2); + + // Add to MI thread-id=\"%d\",stopped-threads=\"all\" + const CMIUtilString strThreadId( + CMIUtilString::Format("%d", thread.GetIndexID())); + const CMICmnMIValueConst miValueConst8(strThreadId); + const CMICmnMIValueResult miValueResult8("thread-id", miValueConst8); + miOutOfBandRecord.Add(miValueResult8); + + const CMICmnMIValueConst miValueConst9("all"); + const CMICmnMIValueResult miValueResult9("stopped-threads", miValueConst9); + miOutOfBandRecord.Add(miValueResult9); + bOk = MiOutOfBandRecordToStdout(miOutOfBandRecord); + bOk = bOk && CMICmnStreamStdout::WritePrompt(); + + return bOk; +} + +//++ +// Details: Asynchronous function update selected thread. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::UpdateSelectedThread() { + lldb::SBProcess process = CMICmnLLDBDebugSessionInfo::Instance() + .GetDebugger() + .GetSelectedTarget() + .GetProcess(); + if (!process.IsValid()) + return MIstatus::success; + + lldb::SBThread currentThread = process.GetSelectedThread(); + lldb::SBThread thread; + const lldb::StopReason eCurrentThreadStoppedReason = + currentThread.GetStopReason(); + if (!currentThread.IsValid() || + (eCurrentThreadStoppedReason == lldb::eStopReasonInvalid) || + (eCurrentThreadStoppedReason == lldb::eStopReasonNone)) { + // Prefer a thread that has just completed its plan over another thread as + // current thread + lldb::SBThread planThread; + lldb::SBThread otherThread; + const size_t nThread = process.GetNumThreads(); + for (MIuint i = 0; i < nThread; i++) { + // GetThreadAtIndex() uses a base 0 index + // GetThreadByIndexID() uses a base 1 index + thread = process.GetThreadAtIndex(i); + const lldb::StopReason eThreadStopReason = thread.GetStopReason(); + switch (eThreadStopReason) { + case lldb::eStopReasonTrace: + case lldb::eStopReasonBreakpoint: + case lldb::eStopReasonWatchpoint: + case lldb::eStopReasonSignal: + case lldb::eStopReasonException: + if (!otherThread.IsValid()) + otherThread = thread; + break; + case lldb::eStopReasonPlanComplete: + if (!planThread.IsValid()) + planThread = thread; + break; + case lldb::eStopReasonInvalid: + case lldb::eStopReasonNone: + default: + break; + } + } + if (planThread.IsValid()) + process.SetSelectedThread(planThread); + else if (otherThread.IsValid()) + process.SetSelectedThread(otherThread); + else { + if (currentThread.IsValid()) + thread = currentThread; + else + thread = process.GetThreadAtIndex(0); + + if (thread.IsValid()) + process.SetSelectedThread(thread); + } + } // if( !currentThread.IsValid() || (eCurrentThreadStoppedReason == + // lldb::eStopReasonInvalid) || (eCurrentThreadStoppedReason == + // lldb::eStopReasonNone) ) + + return MIstatus::success; +} + +//++ +// Details: Print to stdout "*running,thread-id=\"all\"", "(gdb)". +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::HandleProcessEventStateRunning() { + CMICmnMIValueConst miValueConst("all"); + CMICmnMIValueResult miValueResult("thread-id", miValueConst); + CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_Running, miValueResult); + bool bOk = MiOutOfBandRecordToStdout(miOutOfBandRecord); + bOk = bOk && CMICmnStreamStdout::WritePrompt(); + + return bOk; +} + +//++ +// Details: Print to stdout "=thread-exited,id=\"%ld\",group-id=\"i1\"", +// "=thread-group-exited,id=\"i1\",exit-code=\"0\""), +// "*stopped,reason=\"exited-normally\"", +// "(gdb)" +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::HandleProcessEventStateExited() { + const CMIUtilString strId(CMIUtilString::Format("%ld", 1)); + CMICmnMIValueConst miValueConst(strId); + CMICmnMIValueResult miValueResult("id", miValueConst); + CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_ThreadExited, miValueResult); + CMICmnMIValueConst miValueConst2("i1"); + CMICmnMIValueResult miValueResult2("group-id", miValueConst2); + miOutOfBandRecord.Add(miValueResult2); + bool bOk = MiOutOfBandRecordToStdout(miOutOfBandRecord); + if (bOk) { + CMICmnMIValueConst miValueConst3("i1"); + CMICmnMIValueResult miValueResult3("id", miValueConst3); + CMICmnMIOutOfBandRecord miOutOfBandRecord2( + CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupExited, miValueResult3); + CMICmnMIValueConst miValueConst2("0"); + CMICmnMIValueResult miValueResult2("exit-code", miValueConst2); + miOutOfBandRecord2.Add(miValueResult2); + bOk = bOk && MiOutOfBandRecordToStdout(miOutOfBandRecord2); + } + if (bOk) { + CMICmnMIValueConst miValueConst4("exited-normally"); + CMICmnMIValueResult miValueResult4("reason", miValueConst4); + CMICmnMIOutOfBandRecord miOutOfBandRecord3( + CMICmnMIOutOfBandRecord::eOutOfBand_Stopped, miValueResult4); + bOk = MiOutOfBandRecordToStdout(miOutOfBandRecord3); + } + bOk = bOk && CMICmnStreamStdout::WritePrompt(); + + return bOk; +} + +//++ +// Details: Drain all stdout so we don't see any output come after we print our +// prompts. +// The process has stuff waiting for stdout; get it and write it out to +// the +// appropriate place. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::GetProcessStdout() { + CMIUtilString text; + std::unique_ptr apStdoutBuffer(new char[1024]); + lldb::SBProcess process = CMICmnLLDBDebugSessionInfo::Instance() + .GetDebugger() + .GetSelectedTarget() + .GetProcess(); + while (1) { + const size_t nBytes = process.GetSTDOUT(apStdoutBuffer.get(), 1024); + text.append(apStdoutBuffer.get(), nBytes); + + while (1) { + const size_t nNewLine = text.find('\n'); + if (nNewLine == std::string::npos) + break; + + const CMIUtilString line(text.substr(0, nNewLine + 1)); + text.erase(0, nNewLine + 1); + const bool bEscapeQuotes(true); + CMICmnMIValueConst miValueConst(line.Escape(bEscapeQuotes)); + CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_TargetStreamOutput, miValueConst); + const bool bOk = MiOutOfBandRecordToStdout(miOutOfBandRecord); + if (!bOk) + return MIstatus::failure; + } + + if (nBytes == 0) { + if (!text.empty()) { + const bool bEscapeQuotes(true); + CMICmnMIValueConst miValueConst(text.Escape(bEscapeQuotes)); + CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_TargetStreamOutput, + miValueConst); + return MiOutOfBandRecordToStdout(miOutOfBandRecord); + } + break; + } + } + + return MIstatus::success; +} + +//++ +// Details: Drain all stderr so we don't see any output come after we print our +// prompts. +// The process has stuff waiting for stderr; get it and write it out to +// the +// appropriate place. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::GetProcessStderr() { + CMIUtilString text; + std::unique_ptr apStderrBuffer(new char[1024]); + lldb::SBProcess process = CMICmnLLDBDebugSessionInfo::Instance() + .GetDebugger() + .GetSelectedTarget() + .GetProcess(); + while (1) { + const size_t nBytes = process.GetSTDERR(apStderrBuffer.get(), 1024); + text.append(apStderrBuffer.get(), nBytes); + + while (1) { + const size_t nNewLine = text.find('\n'); + if (nNewLine == std::string::npos) + break; + + const CMIUtilString line(text.substr(0, nNewLine + 1)); + const bool bEscapeQuotes(true); + CMICmnMIValueConst miValueConst(line.Escape(bEscapeQuotes)); + CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_TargetStreamOutput, miValueConst); + const bool bOk = MiOutOfBandRecordToStdout(miOutOfBandRecord); + if (!bOk) + return MIstatus::failure; + } + + if (nBytes == 0) { + if (!text.empty()) { + const bool bEscapeQuotes(true); + CMICmnMIValueConst miValueConst(text.Escape(bEscapeQuotes)); + CMICmnMIOutOfBandRecord miOutOfBandRecord( + CMICmnMIOutOfBandRecord::eOutOfBand_TargetStreamOutput, + miValueConst); + return MiOutOfBandRecordToStdout(miOutOfBandRecord); + } + break; + } + } + + return MIstatus::success; +} + +//++ +// Details: Asynchronous event function check for state changes. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::ChkForStateChanges() { + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); + if (!sbProcess.IsValid()) + return MIstatus::success; + + // Check for created threads + const MIuint nThread = sbProcess.GetNumThreads(); + for (MIuint i = 0; i < nThread; i++) { + // GetThreadAtIndex() uses a base 0 index + // GetThreadByIndexID() uses a base 1 index + lldb::SBThread thread = sbProcess.GetThreadAtIndex(i); + if (!thread.IsValid()) + continue; + + const MIuint threadIndexID = thread.GetIndexID(); + const bool bFound = + std::find(rSessionInfo.m_vecActiveThreadId.cbegin(), + rSessionInfo.m_vecActiveThreadId.cend(), + threadIndexID) != rSessionInfo.m_vecActiveThreadId.end(); + if (!bFound) { + rSessionInfo.m_vecActiveThreadId.push_back(threadIndexID); + + // Form MI "=thread-created,id=\"%d\",group-id=\"i1\"" + const CMIUtilString strValue(CMIUtilString::Format("%d", threadIndexID)); + const CMICmnMIValueConst miValueConst(strValue); + const CMICmnMIValueResult miValueResult("id", miValueConst); + CMICmnMIOutOfBandRecord miOutOfBand( + CMICmnMIOutOfBandRecord::eOutOfBand_ThreadCreated, miValueResult); + const CMICmnMIValueConst miValueConst2("i1"); + const CMICmnMIValueResult miValueResult2("group-id", miValueConst2); + miOutOfBand.Add(miValueResult2); + bool bOk = MiOutOfBandRecordToStdout(miOutOfBand); + if (!bOk) + return MIstatus::failure; + } + } + + lldb::SBThread currentThread = sbProcess.GetSelectedThread(); + if (currentThread.IsValid()) { + const MIuint currentThreadIndexID = currentThread.GetIndexID(); + if (rSessionInfo.m_currentSelectedThread != currentThreadIndexID) { + rSessionInfo.m_currentSelectedThread = currentThreadIndexID; + + // Form MI "=thread-selected,id=\"%d\"" + const CMIUtilString strValue( + CMIUtilString::Format("%d", currentThreadIndexID)); + const CMICmnMIValueConst miValueConst(strValue); + const CMICmnMIValueResult miValueResult("id", miValueConst); + CMICmnMIOutOfBandRecord miOutOfBand( + CMICmnMIOutOfBandRecord::eOutOfBand_ThreadSelected, miValueResult); + if (!MiOutOfBandRecordToStdout(miOutOfBand)) + return MIstatus::failure; + } + } + + // Check for invalid (removed) threads + CMICmnLLDBDebugSessionInfo::VecActiveThreadId_t::iterator it = + rSessionInfo.m_vecActiveThreadId.begin(); + while (it != rSessionInfo.m_vecActiveThreadId.end()) { + const MIuint threadIndexID = *it; + lldb::SBThread thread = sbProcess.GetThreadByIndexID(threadIndexID); + if (!thread.IsValid()) { + // Form MI "=thread-exited,id=\"%ld\",group-id=\"i1\"" + const CMIUtilString strValue(CMIUtilString::Format("%ld", threadIndexID)); + const CMICmnMIValueConst miValueConst(strValue); + const CMICmnMIValueResult miValueResult("id", miValueConst); + CMICmnMIOutOfBandRecord miOutOfBand( + CMICmnMIOutOfBandRecord::eOutOfBand_ThreadExited, miValueResult); + const CMICmnMIValueConst miValueConst2("i1"); + const CMICmnMIValueResult miValueResult2("group-id", miValueConst2); + miOutOfBand.Add(miValueResult2); + bool bOk = MiOutOfBandRecordToStdout(miOutOfBand); + if (!bOk) + return MIstatus::failure; + + // Remove current thread from cache and get next + it = rSessionInfo.m_vecActiveThreadId.erase(it); + } else + // Next + ++it; + } + + return CMICmnStreamStdout::WritePrompt(); +} + +//++ +// Details: Take a fully formed MI result record and send to the stdout stream. +// Also output to the MI Log file. +// Type: Method. +// Args: vrMiResultRecord - (R) MI result record object. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::MiResultRecordToStdout( + const CMICmnMIResultRecord &vrMiResultRecord) { + return TextToStdout(vrMiResultRecord.GetString()); +} + +//++ +// Details: Take a fully formed MI Out-of-band record and send to the stdout +// stream. +// Also output to the MI Log file. +// Type: Method. +// Args: vrMiOutOfBandRecord - (R) MI Out-of-band record object. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::MiOutOfBandRecordToStdout( + const CMICmnMIOutOfBandRecord &vrMiOutOfBandRecord) { + return TextToStdout(vrMiOutOfBandRecord.GetString()); +} + +//++ +// Details: Take a text data and send to the stdout stream. Also output to the +// MI Log +// file. +// Type: Method. +// Args: vrTxt - (R) Text. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::TextToStdout(const CMIUtilString &vrTxt) { + return CMICmnStreamStdout::TextToStdout(vrTxt); +} + +//++ +// Details: Take a text data and send to the stderr stream. Also output to the +// MI Log +// file. +// Type: Method. +// Args: vrTxt - (R) Text. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBDebuggerHandleEvents::TextToStderr(const CMIUtilString &vrTxt) { + return CMICmnStreamStderr::TextToStderr(vrTxt); +} + +//++ +// Details: Initialize the member variables with the signal values in this +// process +// file. +// Type: Method. +// Args: None +// Return: Noen +// Throws: None. +//-- +void CMICmnLLDBDebuggerHandleEvents::InitializeSignals() { + if (!m_bSignalsInitialized) { + lldb::SBProcess sbProcess = + CMICmnLLDBDebugSessionInfo::Instance().GetProcess(); + if (sbProcess.IsValid()) { + lldb::SBUnixSignals unix_signals = sbProcess.GetUnixSignals(); + m_SIGINT = unix_signals.GetSignalNumberFromName("SIGINT"); + m_SIGSTOP = unix_signals.GetSignalNumberFromName("SIGSTOP"); + m_SIGSEGV = unix_signals.GetSignalNumberFromName("SIGSEGV"); + m_SIGTRAP = unix_signals.GetSignalNumberFromName("SIGTRAP"); + m_bSignalsInitialized = true; + } + } +} diff --git a/src/MICmnLLDBDebuggerHandleEvents.h b/src/MICmnLLDBDebuggerHandleEvents.h new file mode 100644 index 00000000000..d323bc57c46 --- /dev/null +++ b/src/MICmnLLDBDebuggerHandleEvents.h @@ -0,0 +1,104 @@ +//===-- MICmnLLDBDebuggerHandleEvents.h -------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// 2021.3.15 Add support for OHOS target. +// Copyright (c) 2021 Huawei Device Co., Ltd. All rights reserved. +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmnBase.h" +#include "MICmnMIValueList.h" +#include "MICmnMIValueTuple.h" +#include "MIUtilSingletonBase.h" +#include "lldb/API/SBEvent.h" + +// Declarations: +class CMICmnLLDBDebugSessionInfo; +class CMICmnMIResultRecord; +class CMICmnStreamStdout; +class CMICmnMIOutOfBandRecord; + +//++ +//============================================================================ +// Details: MI class to take LLDB SBEvent objects, filter them and form +// MI Out-of-band records from the information inside the event object. +// These records are then pushed to stdout. +// A singleton class. +//-- +class CMICmnLLDBDebuggerHandleEvents + : public CMICmnBase, + public MI::ISingleton { + friend class MI::ISingleton; + + // Methods: +public: + bool Initialize() override; + bool Shutdown() override; + // + bool HandleEvent(const lldb::SBEvent &vEvent, bool &vrbHandledEvent); + + // Methods: +private: + /* ctor */ CMICmnLLDBDebuggerHandleEvents(); + /* ctor */ CMICmnLLDBDebuggerHandleEvents( + const CMICmnLLDBDebuggerHandleEvents &); + void operator=(const CMICmnLLDBDebuggerHandleEvents &); + // + bool ChkForStateChanges(); + bool GetProcessStdout(); + bool GetProcessStderr(); + bool HandleEventSBBreakPoint(const lldb::SBEvent &vEvent); + bool HandleEventSBBreakpointCmn(const lldb::SBEvent &vEvent); + bool HandleEventSBBreakpointAdded(const lldb::SBEvent &vEvent); + bool HandleEventSBBreakpointLocationsAdded(const lldb::SBEvent &vEvent); + bool HandleEventSBProcess(const lldb::SBEvent &vEvent); + bool HandleEventSBTarget(const lldb::SBEvent &vEvent); + bool HandleEventSBThread(const lldb::SBEvent &vEvent); + bool HandleEventSBThreadBitStackChanged(const lldb::SBEvent &vEvent); + bool HandleEventSBThreadSuspended(const lldb::SBEvent &vEvent); + bool HandleEventSBCommandInterpreter(const lldb::SBEvent &vEvent); + bool HandleProcessEventBroadcastBitStateChanged(const lldb::SBEvent &vEvent); + bool HandleProcessEventStateRunning(); + bool HandleProcessEventStateExited(); + bool HandleProcessEventStateStopped(const lldb::SBEvent &vrEvent, + bool &vwrbShouldBrk); + bool HandleProcessEventStopReasonTrace(); + bool HandleProcessEventStopReasonBreakpoint(); + bool HandleProcessEventStopReasonWatchpoint(); + bool HandleProcessEventStopSignal(const lldb::SBEvent &vrEvent); + bool HandleProcessEventStopException(); + bool HandleProcessEventStateSuspended(const lldb::SBEvent &vEvent); + bool HandleTargetEventBroadcastBitModulesLoaded(const lldb::SBEvent &vEvent); + bool + HandleTargetEventBroadcastBitModulesUnloaded(const lldb::SBEvent &vEvent); + bool MiHelpGetModuleInfo(const lldb::SBModule &vModule, + const bool vbWithExtraFields, + CMICmnMIOutOfBandRecord &vwrMiOutOfBandRecord); + bool MiHelpGetCurrentThreadFrame(CMICmnMIValueTuple &vwrMiValueTuple); + bool MiResultRecordToStdout(const CMICmnMIResultRecord &vrMiResultRecord); + bool + MiOutOfBandRecordToStdout(const CMICmnMIOutOfBandRecord &vrMiResultRecord); + bool MiStoppedAtBreakPoint(const MIuint64 vBrkPtId, + const lldb::SBBreakpoint &vBrkPt); + bool MiStoppedAtWatchPoint(const MIuint64 vBrkPtId, + const lldb::SBBreakpoint &vBrkPt); + bool TextToStdout(const CMIUtilString &vrTxt); + bool TextToStderr(const CMIUtilString &vrTxt); + bool UpdateSelectedThread(); + + // Overridden: +private: + // From CMICmnBase + /* dtor */ ~CMICmnLLDBDebuggerHandleEvents() override; + void InitializeSignals(); + bool m_bSignalsInitialized; + MIuint64 m_SIGINT; + MIuint64 m_SIGSTOP; + MIuint64 m_SIGSEGV; + MIuint64 m_SIGTRAP; +}; diff --git a/src/MICmnLLDBProxySBValue.cpp b/src/MICmnLLDBProxySBValue.cpp new file mode 100644 index 00000000000..14e39cd1f80 --- /dev/null +++ b/src/MICmnLLDBProxySBValue.cpp @@ -0,0 +1,134 @@ +//===-- MICmnLLDBProxySBValue.cpp -------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include + +// Third Party Headers: +#include "lldb/API/SBError.h" + +// In-house headers: +#include "MICmnLLDBDebugSessionInfo.h" +#include "MICmnLLDBProxySBValue.h" +#include "MIUtilString.h" + +//++ +// Details: Retrieve the numerical value from the SBValue object. If the +// function fails +// it could indicate the SBValue object does not represent an internal +// type. +// Type: Static method. +// Args: vrValue - (R) The SBValue object to get a value from. +// vwValue - (W) The numerical value. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBProxySBValue::GetValueAsUnsigned(const lldb::SBValue &vrValue, + MIuint64 &vwValue) { + lldb::SBValue &rValue = const_cast(vrValue); + bool bCompositeType = true; + MIuint64 nFailValue = 0; + MIuint64 nValue = rValue.GetValueAsUnsigned(nFailValue); + if (nValue == nFailValue) { + nFailValue = 5; // Some arbitrary number + nValue = rValue.GetValueAsUnsigned(nFailValue); + if (nValue != nFailValue) { + bCompositeType = false; + vwValue = nValue; + } + } else { + bCompositeType = false; + vwValue = nValue; + } + + return (bCompositeType ? MIstatus::failure : MIstatus::success); +} + +//++ +// Details: Retrieve the numerical value from the SBValue object. If the +// function fails +// it could indicate the SBValue object does not represent an internal +// type. +// Type: Static method. +// Args: vrValue - (R) The SBValue object to get a value from. +// vwValue - (W) The numerical value. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnLLDBProxySBValue::GetValueAsSigned(const lldb::SBValue &vrValue, + MIint64 &vwValue) { + lldb::SBValue &rValue = const_cast(vrValue); + bool bCompositeType = true; + MIuint64 nFailValue = 0; + MIuint64 nValue = rValue.GetValueAsSigned(nFailValue); + if (nValue == nFailValue) { + nFailValue = 5; // Some arbitrary number + nValue = rValue.GetValueAsSigned(nFailValue); + if (nValue != nFailValue) { + bCompositeType = false; + vwValue = nValue; + } + } else { + bCompositeType = false; + vwValue = nValue; + } + + return (bCompositeType ? MIstatus::failure : MIstatus::success); +} + +//++ +// Details: Retrieve the NUL terminated string from the SBValue object if it of +// the type +// unsigned char *. +// Type: Static method. +// Args: vrValue - (R) The SBValue object to get a value from. +// vwCString - (W) The text data '\0' terminated. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed, not suitable type. +// Throws: None. +//-- +bool CMICmnLLDBProxySBValue::GetCString(const lldb::SBValue &vrValue, + CMIUtilString &vwCString) { + lldb::SBValue &rValue = const_cast(vrValue); + const char *pCType = rValue.GetTypeName(); + if (pCType == nullptr) + return MIstatus::failure; + + const char *pType = "unsigned char *"; + if (!CMIUtilString::Compare(pCType, pType)) + return MIstatus::failure; + + const CMIUtilString strAddr(rValue.GetValue()); + MIint64 nNum = 0; + if (!strAddr.ExtractNumber(nNum)) + return MIstatus::failure; + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + lldb::SBProcess sbProcess = rSessionInfo.GetProcess(); + MIuint nBufferSize = 64; + bool bNeedResize = false; + char *pBuffer = static_cast(::malloc(nBufferSize)); + do { + lldb::SBError error; + const size_t nReadSize = sbProcess.ReadCStringFromMemory( + (lldb::addr_t)nNum, pBuffer, nBufferSize, error); + if (nReadSize == (nBufferSize - 1)) { + bNeedResize = true; + nBufferSize = nBufferSize << 1; + pBuffer = static_cast(::realloc(pBuffer, nBufferSize)); + } else + bNeedResize = false; + } while (bNeedResize); + + vwCString = pBuffer; + free((void *)pBuffer); + + return MIstatus::success; +} diff --git a/src/MICmnLLDBProxySBValue.h b/src/MICmnLLDBProxySBValue.h new file mode 100644 index 00000000000..4852b60763e --- /dev/null +++ b/src/MICmnLLDBProxySBValue.h @@ -0,0 +1,34 @@ +//===-- MICmnLLDBProxySBValue.h ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// Third Party Headers: +#include "lldb/API/SBValue.h" + +// In-house headers: +#include "MIDataTypes.h" + +// Declarations: +class CMIUtilString; + +//++ +//============================================================================ +// Details: MI proxy wrapper class to lldb::SBValue. The class provides +// functionality +// to assist in the use of SBValue's particular function usage. +//-- +class CMICmnLLDBProxySBValue { + // Statics: +public: + static bool GetValueAsSigned(const lldb::SBValue &vrValue, MIint64 &vwValue); + static bool GetValueAsUnsigned(const lldb::SBValue &vrValue, + MIuint64 &vwValue); + static bool GetCString(const lldb::SBValue &vrValue, + CMIUtilString &vwCString); +}; diff --git a/src/MICmnLLDBUtilSBValue.cpp b/src/MICmnLLDBUtilSBValue.cpp new file mode 100644 index 00000000000..0e7df52b7b9 --- /dev/null +++ b/src/MICmnLLDBUtilSBValue.cpp @@ -0,0 +1,497 @@ +//===-- MICmnLLDBUtilSBValue.cpp --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Third party headers: +#include "lldb/API/SBTypeSummary.h" +#include + +// In-house headers: +#include "MICmnLLDBDebugSessionInfo.h" +#include "MICmnLLDBUtilSBValue.h" +#include "MICmnMIValueConst.h" +#include "MICmnMIValueTuple.h" +#include "MIUtilString.h" + +static const char *kUnknownValue = "??"; +static const char *kUnresolvedCompositeValue = "{...}"; + +//++ +// Details: CMICmnLLDBUtilSBValue constructor. +// Type: Method. +// Args: vrValue - (R) The LLDB value object. +// vbHandleCharType - (R) True = Yes return text molding to char +// type, +// False = just return data. +// Return: None. +// Throws: None. +//-- +CMICmnLLDBUtilSBValue::CMICmnLLDBUtilSBValue( + const lldb::SBValue &vrValue, const bool vbHandleCharType /* = false */, + const bool vbHandleArrayType /* = true */) + : m_rValue(const_cast(vrValue)), + m_bHandleCharType(vbHandleCharType), + m_bHandleArrayType(vbHandleArrayType) { + m_bValidSBValue = m_rValue.IsValid(); +} + +//++ +// Details: CMICmnLLDBUtilSBValue destructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnLLDBUtilSBValue::~CMICmnLLDBUtilSBValue() {} + +//++ +// Details: Retrieve from the LLDB SB Value object the name of the variable. If +// the name +// is invalid (or the SBValue object invalid) then "??" is returned. +// Type: Method. +// Args: None. +// Return: CMIUtilString - Name of the variable or "??" for unknown. +// Throws: None. +//-- +CMIUtilString CMICmnLLDBUtilSBValue::GetName() const { + const char *pName = m_bValidSBValue ? m_rValue.GetName() : nullptr; + const CMIUtilString text((pName != nullptr) ? pName : CMIUtilString()); + + return text; +} + +//++ +// Details: Retrieve from the LLDB SB Value object the value of the variable +// described in +// text. If the value is invalid (or the SBValue object invalid) then +// "??" is +// returned. +// Type: Method. +// Args: None. +// Return: CMIUtilString - Text description of the variable's value or "??". +// Throws: None. +//-- +CMIUtilString CMICmnLLDBUtilSBValue::GetValue( + const bool vbExpandAggregates /* = false */) const { + if (!m_bValidSBValue) + return kUnknownValue; + + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + bool bPrintExpandAggregates = false; + bPrintExpandAggregates = rSessionInfo.SharedDataRetrieve( + rSessionInfo.m_constStrPrintExpandAggregates, + bPrintExpandAggregates) && + bPrintExpandAggregates; + + const bool bHandleArrayTypeAsSimple = + m_bHandleArrayType && !vbExpandAggregates && !bPrintExpandAggregates; + CMIUtilString value; + const bool bIsSimpleValue = GetSimpleValue(bHandleArrayTypeAsSimple, value); + if (bIsSimpleValue) + return value; + + if (!vbExpandAggregates && !bPrintExpandAggregates) + return kUnresolvedCompositeValue; + + bool bPrintAggregateFieldNames = false; + bPrintAggregateFieldNames = + !rSessionInfo.SharedDataRetrieve( + rSessionInfo.m_constStrPrintAggregateFieldNames, + bPrintAggregateFieldNames) || + bPrintAggregateFieldNames; + + CMICmnMIValueTuple miValueTuple; + const bool bOk = GetCompositeValue(bPrintAggregateFieldNames, miValueTuple); + if (!bOk) + return kUnknownValue; + + value = miValueTuple.GetString(); + return value; +} + +//++ +// Details: Retrieve from the LLDB SB Value object the value of the variable +// described in +// text if it has a simple format (not composite). +// Type: Method. +// Args: vwrValue - (W) The SBValue in a string format. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmnLLDBUtilSBValue::GetSimpleValue(const bool vbHandleArrayType, + CMIUtilString &vwrValue) const { + const MIuint nChildren = m_rValue.GetNumChildren(); + if (nChildren == 0) { + vwrValue = GetValueSummary(!m_bHandleCharType && IsCharType(), kUnknownValue); + return MIstatus::success; + } else if (IsPointerType()) { + vwrValue = + GetValueSummary(!m_bHandleCharType && IsPointeeCharType(), kUnknownValue); + return MIstatus::success; + } else if (IsArrayType()) { + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + bool bPrintCharArrayAsString = false; + bPrintCharArrayAsString = rSessionInfo.SharedDataRetrieve( + rSessionInfo.m_constStrPrintCharArrayAsString, + bPrintCharArrayAsString) && + bPrintCharArrayAsString; + if (bPrintCharArrayAsString && m_bHandleCharType && + IsFirstChildCharType()) { + vwrValue = GetValueSummary(false); + return MIstatus::success; + } else if (vbHandleArrayType) { + vwrValue = CMIUtilString::Format("[%u]", nChildren); + return MIstatus::success; + } + } else { + // Treat composite value which has registered summary + // (for example with AddCXXSummary) as simple value + vwrValue = GetValueSummary(false); + if (!vwrValue.empty()) + return MIstatus::success; + } + + // Composite variable type i.e. struct + return MIstatus::failure; +} + +bool CMICmnLLDBUtilSBValue::GetCompositeValue( + const bool vbPrintFieldNames, CMICmnMIValueTuple &vwrMiValueTuple, + const MIuint vnDepth /* = 1 */) const { + const MIuint nMaxDepth = 10; + const MIuint nChildren = m_rValue.GetNumChildren(); + for (MIuint i = 0; i < nChildren; ++i) { + const lldb::SBValue member = m_rValue.GetChildAtIndex(i); + const CMICmnLLDBUtilSBValue utilMember(member, m_bHandleCharType, + m_bHandleArrayType); + const bool bHandleArrayTypeAsSimple = false; + CMIUtilString value; + const bool bIsSimpleValue = + utilMember.GetSimpleValue(bHandleArrayTypeAsSimple, value); + if (bIsSimpleValue) { + // OK. Value is simple (not composite) and was successfully got + } else if (vnDepth < nMaxDepth) { + // Need to get value from composite type + CMICmnMIValueTuple miValueTuple; + const bool bOk = utilMember.GetCompositeValue(vbPrintFieldNames, + miValueTuple, vnDepth + 1); + if (!bOk) + // Can't obtain composite type + value = kUnknownValue; + else + // OK. Value is composite and was successfully got + value = miValueTuple.GetString(); + } else { + // Need to get value from composite type, but vnMaxDepth is reached + value = kUnresolvedCompositeValue; + } + const bool bNoQuotes = true; + const CMICmnMIValueConst miValueConst(value, bNoQuotes); + if (vbPrintFieldNames) { + const bool bUseSpacing = true; + const CMICmnMIValueResult miValueResult(utilMember.GetName(), + miValueConst, bUseSpacing); + vwrMiValueTuple.Add(miValueResult, bUseSpacing); + } else { + const bool bUseSpacing = false; + vwrMiValueTuple.Add(miValueConst, bUseSpacing); + } + } + + return MIstatus::success; +} + +// Returns value or value + summary, depending on valueOnly parameter value. +// If result is an empty string returns failVal. +CMIUtilString +CMICmnLLDBUtilSBValue::GetValueSummary(bool valueOnly, + const CMIUtilString &failVal) const { + if (!m_rValue.IsValid()) + return failVal; + + CMIUtilString value, valSummary; + const char *c_value = m_rValue.GetValue(); + if (valueOnly) + return c_value == nullptr ? failVal : c_value; + + const char *c_summary = m_rValue.GetSummary(); + if (c_value) + value = c_value; + else if (c_summary == nullptr) + return failVal; + + if (c_summary && c_summary[0]) { + valSummary = c_summary; + lldb::SBTypeSummary summary = m_rValue.GetTypeSummary(); + if (summary.IsValid() && summary.DoesPrintValue(m_rValue) && + !value.empty()) { + valSummary.insert(0, value + " "); + } + return valSummary; + } + // no summary - return just value + return value; +} + +//++ +// Details: Check that basic type is a char type. Char type can be signed or +// unsigned. +// Type: Static. +// Args: eType - type to check +// Return: bool - True = Yes is a char type, false = some other type. +// Throws: None. +//-- +bool CMICmnLLDBUtilSBValue::IsCharBasicType(lldb::BasicType eType) { + switch (eType) { + case lldb::eBasicTypeChar: + case lldb::eBasicTypeSignedChar: + case lldb::eBasicTypeUnsignedChar: + case lldb::eBasicTypeChar16: + case lldb::eBasicTypeChar32: + return true; + default: + return false; + } +} + +//++ +// Details: Retrieve the flag stating whether this value object is a char type +// or some +// other type. Char type can be signed or unsigned. +// Type: Method. +// Args: None. +// Return: bool - True = Yes is a char type, false = some other type. +// Throws: None. +//-- +bool CMICmnLLDBUtilSBValue::IsCharType() const { + const lldb::BasicType eType = m_rValue.GetType().GetBasicType(); + return IsCharBasicType(eType); +} + +//++ +// Details: Retrieve the flag stating whether first child value object of *this +// object is +// a char type or some other type. Returns false if there are not +// children. Char +// type can be signed or unsigned. +// Type: Method. +// Args: None. +// Return: bool - True = Yes is a char type, false = some other type. +// Throws: None. +//-- +bool CMICmnLLDBUtilSBValue::IsFirstChildCharType() const { + const MIuint nChildren = m_rValue.GetNumChildren(); + + // Is it a basic type + if (nChildren == 0) + return false; + + const lldb::SBValue member = m_rValue.GetChildAtIndex(0); + const CMICmnLLDBUtilSBValue utilValue(member); + return utilValue.IsCharType(); +} + +//++ +// Details: Retrieve the flag stating whether pointee object of *this object is +// a char type or some other type. Returns false if there are not +// children. Char +// type can be signed or unsigned. +// Type: Method. +// Args: None. +// Return: bool - True = Yes is a char type, false = some other type. +// Throws: None. +//-- +bool CMICmnLLDBUtilSBValue::IsPointeeCharType() const { + const MIuint nChildren = m_rValue.GetNumChildren(); + + // Is it a basic type + if (nChildren == 0) + return false; + + const lldb::BasicType eType = + m_rValue.GetType().GetPointeeType().GetBasicType(); + return IsCharBasicType(eType); +} + +//++ +// Details: Retrieve the flag stating whether this value object is a integer +// type or some +// other type. Char type can be signed or unsigned and short or +// long/very long. +// Type: Method. +// Args: None. +// Return: bool - True = Yes is a integer type, false = some other type. +// Throws: None. +//-- +bool CMICmnLLDBUtilSBValue::IsIntegerType() const { + const lldb::BasicType eType = m_rValue.GetType().GetBasicType(); + return ((eType == lldb::eBasicTypeShort) || + (eType == lldb::eBasicTypeUnsignedShort) || + (eType == lldb::eBasicTypeInt) || + (eType == lldb::eBasicTypeUnsignedInt) || + (eType == lldb::eBasicTypeLong) || + (eType == lldb::eBasicTypeUnsignedLong) || + (eType == lldb::eBasicTypeLongLong) || + (eType == lldb::eBasicTypeUnsignedLongLong) || + (eType == lldb::eBasicTypeInt128) || + (eType == lldb::eBasicTypeUnsignedInt128)); +} + +//++ +// Details: Retrieve the flag stating whether this value object is a pointer +// type or some +// other type. +// Type: Method. +// Args: None. +// Return: bool - True = Yes is a pointer type, false = some other type. +// Throws: None. +//-- +bool CMICmnLLDBUtilSBValue::IsPointerType() const { + return m_rValue.GetType().IsPointerType(); +} + +//++ +// Details: Retrieve the flag stating whether this value object is an array type +// or some +// other type. +// Type: Method. +// Args: None. +// Return: bool - True = Yes is an array type, false = some other type. +// Throws: None. +//-- +bool CMICmnLLDBUtilSBValue::IsArrayType() const { + return m_rValue.GetType().IsArrayType(); +} + +//++ +// Details: Retrieve the C string data of value object by read the memory where +// the +// variable is held. +// Type: Method. +// Args: vrValue - (R) LLDB SBValue variable object. +// Return: CMIUtilString - Text description of the variable's value. +// Throws: None. +//-- +template +CMIUtilString +CMICmnLLDBUtilSBValue::ReadCStringFromHostMemory(lldb::SBValue &vrValue, + const MIuint vnMaxLen) const { + std::string result; + lldb::addr_t addr = vrValue.GetLoadAddress(), + end_addr = addr + vnMaxLen * sizeof(charT); + lldb::SBProcess process = CMICmnLLDBDebugSessionInfo::Instance().GetProcess(); + lldb::SBError error; + while (addr < end_addr) { + charT ch; + const MIuint64 nReadBytes = + process.ReadMemory(addr, &ch, sizeof(ch), error); + if (error.Fail() || nReadBytes != sizeof(ch)) + return kUnknownValue; + else if (ch == 0) + break; + result.append( + CMIUtilString::ConvertToPrintableASCII(ch, true /* bEscapeQuotes */)); + addr += sizeof(ch); + } + + return result; +} + +//++ +// Details: Retrieve the state of the value object's name. +// Type: Method. +// Args: None. +// Return: bool - True = yes name is indeterminate, false = name is valid. +// Throws: None. +//-- +bool CMICmnLLDBUtilSBValue::IsNameUnknown() const { + const CMIUtilString name(GetName()); + return (name == kUnknownValue); +} + +//++ +// Details: Retrieve the state of the value object's value data. +// Type: Method. +// Args: None. +// Return: bool - True = yes value is indeterminate, false = value valid. +// Throws: None. +//-- +bool CMICmnLLDBUtilSBValue::IsValueUnknown() const { + const CMIUtilString value(GetValue()); + return (value == kUnknownValue); +} + +//++ +// Details: Retrieve the value object's type name if valid. +// Type: Method. +// Args: None. +// Return: CMIUtilString - The type name or "??". +// Throws: None. +//-- +CMIUtilString CMICmnLLDBUtilSBValue::GetTypeName() const { + const char *pName = m_bValidSBValue ? m_rValue.GetTypeName() : nullptr; + const CMIUtilString text((pName != nullptr) ? pName : kUnknownValue); + + return text; +} + +//++ +// Details: Retrieve the value object's display type name if valid. +// Type: Method. +// Args: None. +// Return: CMIUtilString - The type name or "??". +// Throws: None. +//-- +CMIUtilString CMICmnLLDBUtilSBValue::GetTypeNameDisplay() const { + const char *pName = m_bValidSBValue ? m_rValue.GetDisplayTypeName() : nullptr; + const CMIUtilString text((pName != nullptr) ? pName : kUnknownValue); + + return text; +} + +//++ +// Details: Retrieve whether the value object's is valid or not. +// Type: Method. +// Args: None. +// Return: bool - True = valid, false = not valid. +// Throws: None. +//-- +bool CMICmnLLDBUtilSBValue::IsValid() const { return m_bValidSBValue; } + +//++ +// Details: Retrieve the value object' has a name. A value object can be valid +// but still +// have no name which suggest it is not a variable. +// Type: Method. +// Args: None. +// Return: bool - True = valid, false = not valid. +// Throws: None. +//-- +bool CMICmnLLDBUtilSBValue::HasName() const { + bool bHasAName = false; + + const char *pName = m_bValidSBValue ? m_rValue.GetDisplayTypeName() : nullptr; + if (pName != nullptr) { + bHasAName = (CMIUtilString(pName).length() > 0); + } + + return bHasAName; +} + +//++ +// Details: Determine if the value object' represents a LLDB variable i.e. "$0". +// Type: Method. +// Args: None. +// Return: bool - True = Yes LLDB variable, false = no. +// Throws: None. +//-- +bool CMICmnLLDBUtilSBValue::IsLLDBVariable() const { + return (GetName().at(0) == '$'); +} diff --git a/src/MICmnLLDBUtilSBValue.h b/src/MICmnLLDBUtilSBValue.h new file mode 100644 index 00000000000..6804402b75d --- /dev/null +++ b/src/MICmnLLDBUtilSBValue.h @@ -0,0 +1,77 @@ +//===-- MICmnLLDBUtilSBValue.h ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// Third Party Headers: +#include "lldb/API/SBValue.h" + +// In-house headers: +#include "MICmnMIValueTuple.h" +#include "MIDataTypes.h" + +// Declarations: +class CMIUtilString; + +//++ +//============================================================================ +// Details: Utility helper class to lldb::SBValue. Using a lldb::SBValue extract +// value object information to help form verbose debug information. +//-- +class CMICmnLLDBUtilSBValue { + // Methods: +public: + /* ctor */ CMICmnLLDBUtilSBValue(const lldb::SBValue &vrValue, + const bool vbHandleCharType = false, + const bool vbHandleArrayType = true); + /* dtor */ ~CMICmnLLDBUtilSBValue(); + // + CMIUtilString GetName() const; + CMIUtilString GetValue(const bool vbExpandAggregates = false) const; + CMIUtilString GetTypeName() const; + CMIUtilString GetTypeNameDisplay() const; + bool IsCharType() const; + bool IsFirstChildCharType() const; + bool IsPointeeCharType() const; + bool IsIntegerType() const; + bool IsPointerType() const; + bool IsArrayType() const; + bool IsLLDBVariable() const; + bool IsNameUnknown() const; + bool IsValueUnknown() const; + bool IsValid() const; + bool HasName() const; + + // Methods: +private: + template + CMIUtilString + ReadCStringFromHostMemory(lldb::SBValue &vrValue, + const MIuint vnMaxLen = UINT32_MAX) const; + bool GetSimpleValue(const bool vbHandleArrayType, + CMIUtilString &vrValue) const; + bool GetCompositeValue(const bool vbPrintFieldNames, + CMICmnMIValueTuple &vwrMiValueTuple, + const MIuint vnDepth = 1) const; + CMIUtilString + GetValueSummary(bool valueOnly, + const CMIUtilString &failVal = CMIUtilString()) const; + + // Statics: +private: + static bool IsCharBasicType(lldb::BasicType eType); + + // Attributes: +private: + lldb::SBValue &m_rValue; + bool m_bValidSBValue; // True = SBValue is a valid object, false = not valid. + bool m_bHandleCharType; // True = Yes return text molding to char type, false + // = just return data. + bool m_bHandleArrayType; // True = Yes return special stub for array type, + // false = just return data. +}; diff --git a/src/MICmnLog.cpp b/src/MICmnLog.cpp new file mode 100644 index 00000000000..fc4c3141915 --- /dev/null +++ b/src/MICmnLog.cpp @@ -0,0 +1,330 @@ +//===-- MICmnLog.cpp --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmnLog.h" +#include "MICmnLogMediumFile.h" +#include "MICmnResources.h" +#include "MIDriverMgr.h" +#include "MIUtilDateTimeStd.h" + +//++ +// Details: CMICmnLog constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnLog::CMICmnLog() : m_bEnabled(false), m_bInitializingATM(false) { + // Do not use this constructor, use Initialize() +} + +//++ +// Details: CMICmnLog destructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnLog::~CMICmnLog() { Shutdown(); } + +//++ +// Details: Initialize resources for *this Logger. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLog::Initialize() { + m_clientUsageRefCnt++; + + if (m_bInitialized) + return MIstatus::success; + + ClrErrorDescription(); + + // Mediums set inside because explicitly initing in MIDriverMain.cpp causes + // compile errors with CAtlFile + CMICmnLogMediumFile &rFileLog(CMICmnLogMediumFile::Instance()); + bool bOk = RegisterMedium(rFileLog); + if (bOk) { + // Set the Log trace file's header + const CMIUtilString &rCR(rFileLog.GetLineReturn()); + CMIUtilDateTimeStd date; + CMIUtilString msg; + msg = CMIUtilString::Format( + "%s\n", CMIDriverMgr::Instance().GetAppVersion().c_str()); + CMIUtilString logHdr(msg); + msg = CMIUtilString::Format(MIRSRC(IDS_LOG_MSG_CREATION_DATE), + date.GetDate().c_str(), date.GetTime().c_str(), + rCR.c_str()); + logHdr += msg; + msg = + CMIUtilString::Format(MIRSRC(IDS_LOG_MSG_FILE_LOGGER_PATH), + rFileLog.GetFileNamePath().c_str(), rCR.c_str()); + logHdr += msg; + + bOk = rFileLog.SetHeaderTxt(logHdr); + + // Note log file medium's status is not available until we write at least + // once to the file (so just write the title 1st line) + m_bInitializingATM = true; + CMICmnLog::WriteLog("."); + if (!rFileLog.IsOk()) { + const CMIUtilString msg( + CMIUtilString::Format(MIRSRC(IDS_LOG_ERR_FILE_LOGGER_DISABLED), + rFileLog.GetErrorDescription().c_str())); + CMICmnLog::WriteLog(msg); + } + m_bInitializingATM = false; + } + + m_bInitialized = bOk; + + return bOk; +} + +//++ +// Details: Release resources for *this Logger. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLog::Shutdown() { + if (--m_clientUsageRefCnt > 0) + return MIstatus::success; + + if (!m_bInitialized) + return MIstatus::success; + + ClrErrorDescription(); + + const bool bOk = UnregisterMediumAll(); + + m_bInitialized = bOk; + + return bOk; +} + +//++ +// Details: Enabled or disable *this Logger from writing any data to registered +// clients. +// Type: Method. +// Args: vbYes - (R) True = Logger enabled, false = disabled. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLog::SetEnabled(const bool vbYes) { + m_bEnabled = vbYes; + + return MIstatus::success; +} + +//++ +// Details: Retrieve state whether *this Logger is enabled writing data to +// registered clients. +// Type: Method. +// Args: None. +// Return: True = Logger enable. +// False = disabled. +// Throws: None. +//-- +bool CMICmnLog::GetEnabled() const { return m_bEnabled; } + +//++ +// Details: Unregister all the Mediums registered with *this Logger. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLog::UnregisterMediumAll() { + MapMediumToName_t::const_iterator it = m_mapMediumToName.begin(); + for (; it != m_mapMediumToName.end(); it++) { + IMedium *pMedium = (*it).first; + pMedium->Shutdown(); + } + + m_mapMediumToName.clear(); + + return MIstatus::success; +} + +//++ +// Details: Register a Medium with *this Logger. +// Type: Method. +// Args: vrMedium - (R) The medium to register. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLog::RegisterMedium(const IMedium &vrMedium) { + if (HaveMediumAlready(vrMedium)) + return MIstatus::success; + + IMedium *pMedium = const_cast(&vrMedium); + if (!pMedium->Initialize()) { + const CMIUtilString &rStrMedName(pMedium->GetName()); + const CMIUtilString &rStrMedErr(pMedium->GetError()); + SetErrorDescription(CMIUtilString::Format(MIRSRC(IDS_LOG_MEDIUM_ERR_INIT), + rStrMedName.c_str(), + rStrMedErr.c_str())); + return MIstatus::failure; + } + + MapPairMediumToName_t pr(pMedium, pMedium->GetName()); + m_mapMediumToName.insert(pr); + + return MIstatus::success; +} + +//++ +// Details: Query the Logger to see if a medium is already registered. +// Type: Method. +// Args: vrMedium - (R) The medium to query. +// Return: True - registered. +// False - not registered. +// Throws: None. +//-- +bool CMICmnLog::HaveMediumAlready(const IMedium &vrMedium) const { + IMedium *pMedium = const_cast(&vrMedium); + const MapMediumToName_t::const_iterator it = m_mapMediumToName.find(pMedium); + return it != m_mapMediumToName.end(); +} + +//++ +// Details: Unregister a medium from the Logger. +// Type: Method. +// Args: vrMedium - (R) The medium to unregister. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLog::UnregisterMedium(const IMedium &vrMedium) { + IMedium *pMedium = const_cast(&vrMedium); + m_mapMediumToName.erase(pMedium); + + return MIstatus::success; +} + +//++ +// Details: The callee client uses this function to write to the Logger. The +// data to be +// written is given out to all the mediums registered. The verbosity +// type parameter +// indicates to the medium(s) the type of data or message given to it. +// The medium has +// modes of verbosity and depending on the verbosity set determines +// which writes +// go in to the logger. +// The logger must be initialized successfully before a write to any +// registered +// can be carried out. +// Type: Method. +// Args: vData - (R) The data to write to the logger. +// veType - (R) Verbosity type. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLog::Write(const CMIUtilString &vData, const ELogVerbosity veType) { + if (!m_bInitialized && !m_bInitializingATM) + return MIstatus::success; + if (m_bRecursiveDive) + return MIstatus::success; + if (!m_bEnabled) + return MIstatus::success; + + m_bRecursiveDive = true; + + MIuint cnt = 0; + MIuint cntErr = 0; + { + MapMediumToName_t::const_iterator it = m_mapMediumToName.begin(); + while (it != m_mapMediumToName.end()) { + IMedium *pMedium = (*it).first; + const CMIUtilString &rNameMedium = (*it).second; + MIunused(rNameMedium); + if (pMedium->Write(vData, veType)) + cnt++; + else + cntErr++; + + // Next + ++it; + } + } + + bool bOk = MIstatus::success; + const MIuint mediumCnt = m_mapMediumToName.size(); + if ((cnt == 0) && (mediumCnt > 0)) { + SetErrorDescription(MIRSRC(IDS_LOG_MEDIUM_ERR_WRITE_ANY)); + bOk = MIstatus::failure; + } + if (bOk && (cntErr != 0)) { + SetErrorDescription(MIRSRC(IDS_LOG_MEDIUM_ERR_WRITE_MEDIUMFAIL)); + bOk = MIstatus::failure; + } + + m_bRecursiveDive = false; + + return bOk; +} + +//++ +// Details: Short cut function call to write only to the Log file. +// The logger must be initialized successfully before a write to any +// registered +// can be carried out. +// Type: Static. +// Args: vData - (R) The data to write to the logger. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLog::WriteLog(const CMIUtilString &vData) { + return CMICmnLog::Instance().Write(vData, CMICmnLog::eLogVerbosity_Log); +} + +//++ +// Details: Retrieve a string detailing the last error. +// Type: Method. +// Args: None, +// Return: CMIUtilString. +// Throws: None. +//-- +const CMIUtilString &CMICmnLog::GetErrorDescription() const { + return m_strMILastErrorDescription; +} + +//++ +// Details: Set the internal description of the last error. +// Type: Method. +// Args: (R) String containing a description of the last error. +// Return: None. +// Throws: None. +//-- +void CMICmnLog::SetErrorDescription(const CMIUtilString &vrTxt) const { + m_strMILastErrorDescription = vrTxt; +} + +//++ +// Details: Clear the last error. +// Type: None. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMICmnLog::ClrErrorDescription() const { + m_strMILastErrorDescription = CMIUtilString(""); +} diff --git a/src/MICmnLog.h b/src/MICmnLog.h new file mode 100644 index 00000000000..909dc613b02 --- /dev/null +++ b/src/MICmnLog.h @@ -0,0 +1,138 @@ +//===-- MICmnLog.h ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// Third party headers: +#include + +// In-house headers: +#include "MICmnBase.h" +#include "MIUtilSingletonBase.h" +#include "MIUtilString.h" + +//++ +//============================================================================ +// Details: MI common code implementation class. Handle application trace +// activity logging. Medium objects derived from the Medium abstract +/// class are registered with this logger. The function Write is called +// by a client callee to log information. That information is given to +// registered relevant mediums. The medium file is registered during +// *this logs initialization so it will always have a file log for the +// application. +// Singleton class. +//-- +class CMICmnLog : public MI::ISingleton { + friend MI::ISingleton; + + // Enumeration: +public: + //++ + // Description: Data given to the Logger can be of several types. The Logger + // can be + // set at levels of verbosity. Can determine how data is sent to + // one or + // mediums. + //-- + enum ELogVerbosity { // Descriptions of what 'may' occur, depends ultimately + // on the medium itself. See the medium. + eLogVerbosity_FnTrace = 0x00000004, // Debug function stack call tracing + eLogVerbosity_DbgOp = 0x00000008, // Send a string to the debugger for + // display (not implemented) + eLogVerbosity_ClientMsg = 0x00000010, // A client using MI can insert + // messages into the log (not + // implemented) + eLogVerbosity_Log = 0x00000020 // Send to only the Log file. + }; + + // Class: +public: + //++ + // Description: Register a medium derived from this interface which will be + // called writing log trace data i.e. a file or a console. + // Medium objects registered are not owned by *this logger. + //-- + class IMedium { + public: + virtual bool Initialize() = 0; + virtual const CMIUtilString &GetName() const = 0; + virtual bool Write(const CMIUtilString &vData, + const ELogVerbosity veType) = 0; + virtual const CMIUtilString &GetError() const = 0; + virtual bool Shutdown() = 0; + + // Not part of the interface, ignore + // AD: This virtual destructor seems to hit a bug in the stdlib + // where vector delete is incorrectly called. Workaround is + // to comment this out while I investigate. + /* dtor */ virtual ~IMedium() {} + }; + + // Statics: +public: + static bool WriteLog(const CMIUtilString &vData); + + // Methods: +public: + bool RegisterMedium(const IMedium &vrMedium); + bool UnregisterMedium(const IMedium &vrMedium); + bool Write(const CMIUtilString &vData, const ELogVerbosity veType); + bool SetEnabled(const bool vbYes); + bool GetEnabled() const; + + // MI common object handling - duplicate of CMICmnBase functions, necessary + // for LINUX build + // Done to stop locking on object construction init circular dependency. + const CMIUtilString &GetErrorDescription() const; + void SetErrorDescription(const CMIUtilString &vrTxt) const; + void ClrErrorDescription() const; + + // Overridden: +public: + // From MI::ISingleton + bool Initialize() override; + bool Shutdown() override; + + // Methods: +private: + /* ctor */ CMICmnLog(); + /* ctor */ CMICmnLog(const CMICmnLog &); + void operator=(const CMICmnLog &); + + // Overridden: +private: + // From CMICmnBase + /* dtor */ ~CMICmnLog() override; + + // Typedef: +private: + typedef std::map MapMediumToName_t; + typedef std::pair MapPairMediumToName_t; + + // Methods: +private: + bool HaveMediumAlready(const IMedium &vrMedium) const; + bool UnregisterMediumAll(); + + // Attributes: +private: + bool m_bRecursiveDive; // True = yes recursive, false = no + MapMediumToName_t m_mapMediumToName; + bool m_bEnabled; // True = Logger enabled for writing to mediums, false = + // medium not written to + bool m_bInitializingATM; // True = Yes in process of initing *this logger, + // false = not initing + // + // MI common object handling - duplicate of CMICmnBase functions, necessary + // for LINUX build + bool m_bInitialized; // True = yes successfully initialized, false = no yet or + // failed + mutable CMIUtilString m_strMILastErrorDescription; + MIint m_clientUsageRefCnt; // Count of client using *this object so not + // shutdown() object to early +}; diff --git a/src/MICmnLogMediumFile.cpp b/src/MICmnLogMediumFile.cpp new file mode 100644 index 00000000000..a8704b285ed --- /dev/null +++ b/src/MICmnLogMediumFile.cpp @@ -0,0 +1,387 @@ +//===-- MICmnLogMediumFile.cpp ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// 2021.3.15 Add support for OHOS target. +// Copyright (c) 2021 Huawei Device Co., Ltd. All rights reserved. +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmnLogMediumFile.h" +#include "MICmnResources.h" + +//++ +// Details: CMICmnLogMediumFile constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnLogMediumFile::CMICmnLogMediumFile() + : m_constThisMediumName(MIRSRC(IDS_MEDIUMFILE_NAME)), + m_constMediumFileNameFormat("lldb-mi-%s.log"), + m_strMediumFileName(MIRSRC(IDS_MEDIUMFILE_ERR_INVALID_PATH)), + m_strMediumFileDirectory("."), + m_fileNamePath(MIRSRC(IDS_MEDIUMFILE_ERR_INVALID_PATH)), + m_eVerbosityType(CMICmnLog::eLogVerbosity_Log), + m_strDate(CMIUtilDateTimeStd().GetDate()), + m_fileHeaderTxt(MIRSRC(IDS_MEDIUMFILE_ERR_FILE_HEADER)) {} + +//++ +// Details: CMICmnLogMediumFile destructor. +// Type: Overridden. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnLogMediumFile::~CMICmnLogMediumFile() {} + +//++ +// Details: Get the singleton instance of *this class. +// Type: Static. +// Args: None. +// Return: CMICmnLogMediumFile - Reference to *this object. +// Throws: None. +//-- +CMICmnLogMediumFile &CMICmnLogMediumFile::Instance() { + static CMICmnLogMediumFile instance; + + return instance; +} + +//++ +// Details: Initialize setup *this medium ready for use. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLogMediumFile::Initialize() { + m_bInitialized = true; + return FileFormFileNamePath(); +} + +//++ +// Details: Unbind detach or release resources used by *this medium. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +bool CMICmnLogMediumFile::Shutdown() { + if (m_bInitialized) { + m_bInitialized = false; + m_file.Close(); + } + return MIstatus::success; +} + +//++ +// Details: Retrieve the name of *this medium. +// Type: Overridden. +// Args: None. +// Return: CMIUtilString - Text data. +// Throws: None. +//-- +const CMIUtilString &CMICmnLogMediumFile::GetName() const { + return m_constThisMediumName; +} + +//++ +// Details: The callee client calls the write function on the Logger. The data +// to be +// written is given out to all the mediums registered. The verbosity +// type parameter +// indicates to the medium the type of data or message given to it. The +// medium has +// modes of verbosity and depending on the verbosity set determines +// which data is +// sent to the medium's output. +// Type: Method. +// Args: vData - (R) The data to write to the logger. +// veType - (R) Verbosity type. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLogMediumFile::Write(const CMIUtilString &vData, + const CMICmnLog::ELogVerbosity veType) { + if (m_bInitialized && m_file.IsOk()) { + const bool bDoWrite = (m_eVerbosityType & veType); + if (bDoWrite) { + bool bNewCreated = false; + bool bOk = m_file.CreateWrite(m_fileNamePath, bNewCreated); + if (bOk) { + if (bNewCreated) + bOk = FileWriteHeader(); + bOk = bOk && FileWriteEnglish(MassagedData(vData, veType)); + } + return bOk; + } + } + + return MIstatus::failure; +} + +//++ +// Details: Retrieve *this medium's last error condition. +// Type: Method. +// Args: None. +// Return: CString & - Text description. +// Throws: None. +//-- +const CMIUtilString &CMICmnLogMediumFile::GetError() const { + return m_strMILastErrorDescription; +} + +//++ +// Details: Set the verbosity mode for this medium. +// Type: Method. +// Args: veType - (R) Mask value. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLogMediumFile::SetVerbosity(const MIuint veType) { + m_eVerbosityType = veType; + return MIstatus::success; +} + +//++ +// Details: Get the verbosity mode for this medium. +// Type: Method. +// Args: veType - (R) Mask value. +// Return: CMICmnLog::ELogVerbosity - Mask value. +// Throws: None. +//-- +MIuint CMICmnLogMediumFile::GetVerbosity() const { return m_eVerbosityType; } + +//++ +// Details: Write data to a file English font. +// Type: Method. +// Args: vData - (R) The data to write to the logger. +// Return: None. +// Throws: None. +//-- +bool CMICmnLogMediumFile::FileWriteEnglish(const CMIUtilString &vData) { + return m_file.Write(vData); +} + +//++ +// Details: Determine and form the medium file's directory path and name. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLogMediumFile::FileFormFileNamePath() { + ClrErrorDescription(); + + m_fileNamePath = MIRSRC(IDS_MEDIUMFILE_ERR_INVALID_PATH); + + CMIUtilDateTimeStd date; + m_strMediumFileName = + CMIUtilString::Format(m_constMediumFileNameFormat.c_str(), + date.GetDateTimeLogFilename().c_str()); + +#if defined(_WIN32) + m_fileNamePath = CMIUtilString::Format( + "%s\\%s", m_strMediumFileDirectory.c_str(), m_strMediumFileName.c_str()); +#else + m_fileNamePath = CMIUtilString::Format( + "%s/%s", m_strMediumFileDirectory.c_str(), m_strMediumFileName.c_str()); +#endif // defined ( _WIN32 ) + + return MIstatus::success; +} + +//++ +// Details: Retrieve the medium file's directory path and name. +// Type: Method. +// Args: None. +// Return: CMIUtilString & - File path. +// Throws: None. +//-- +const CMIUtilString &CMICmnLogMediumFile::GetFileNamePath() const { + return m_fileNamePath; +} + +//++ +// Details: Retrieve the medium file's name. +// Type: Method. +// Args: None. +// Return: CMIUtilString & - File name. +// Throws: None. +//-- +const CMIUtilString &CMICmnLogMediumFile::GetFileName() const { + return m_strMediumFileName; +} + +//++ +// Details: Massage the data to behave correct when submitted to file. Insert +// extra log +// specific text. The veType is there to allow in the future to parse +// the log and +// filter in out specific types of message to make viewing the log more +// manageable. +// Type: Method. +// Args: vData - (R) Raw data. +// veType - (R) Message type. +// Return: CMIUtilString - Massaged data. +// Throws: None. +//-- +CMIUtilString +CMICmnLogMediumFile::MassagedData(const CMIUtilString &vData, + const CMICmnLog::ELogVerbosity veType) { + const CMIUtilString strCr("\n"); + CMIUtilString data; + const char verbosityCode(ConvertLogVerbosityTypeToId(veType)); + const CMIUtilString dt(CMIUtilString::Format("%s %s", m_strDate.c_str(), + m_dateTime.GetTime().c_str())); + + data = CMIUtilString::Format("%c,%s,%s", verbosityCode, dt.c_str(), + vData.c_str()); + data = ConvertCr(data); + + // Look for EOL... + const size_t pos = vData.rfind(strCr); + if (pos == vData.size()) + return data; + + // ... did not have an EOL so add one + data += GetLineReturn(); + + return data; +} + +//++ +// Details: Convert the Log's verbosity type number into a single char +// character. +// Type: Method. +// Args: veType - (R) Message type. +// Return: wchar_t - A letter. +// Throws: None. +//-- +char CMICmnLogMediumFile::ConvertLogVerbosityTypeToId( + const CMICmnLog::ELogVerbosity veType) const { + char c = 0; + if (veType != 0) { + MIuint cnt = 0; + MIuint number(veType); + while (1 != number) { + number = number >> 1; + ++cnt; + } + c = 'A' + cnt; + } else { + c = '*'; + } + + return c; +} + +//++ +// Details: Retrieve state of whether the file medium is ok. +// Type: Method. +// Args: None. +// Return: True - file ok. +// False - file has a problem. +// Throws: None. +//-- +bool CMICmnLogMediumFile::IsOk() const { return m_file.IsOk(); } + +//++ +// Details: Status on the file log medium existing already. +// Type: Method. +// Args: None. +// Return: True - Exists. +// False - Not found. +// Throws: None. +//-- +bool CMICmnLogMediumFile::IsFileExist() const { + return m_file.IsFileExist(GetFileNamePath()); +} + +//++ +// Details: Write the header text the logger file. +// Type: Method. +// Args: vText - (R) Text. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLogMediumFile::FileWriteHeader() { + return FileWriteEnglish(ConvertCr(m_fileHeaderTxt)); +} + +//++ +// Details: Convert any carriage line returns to be compatible with the platform +// the +// Log file is being written to. +// Type: Method. +// Args: vData - (R) Text data. +// Return: CMIUtilString - Converted string data. +// Throws: None. +//-- +CMIUtilString CMICmnLogMediumFile::ConvertCr(const CMIUtilString &vData) const { + const CMIUtilString strCr("\n"); + const CMIUtilString &rCrCmpat(GetLineReturn()); + + if (strCr == rCrCmpat) + return vData; + + const size_t nSizeCmpat(rCrCmpat.size()); + const size_t nSize(strCr.size()); + CMIUtilString strConv(vData); + size_t pos = strConv.find(strCr); + while (pos != CMIUtilString::npos) { + strConv.replace(pos, nSize, rCrCmpat); + pos = strConv.find(strCr, pos + nSizeCmpat); + } + + return strConv; +} + +//++ +// Details: Set the header text that is written to the logger file at the +// beginning. +// Type: Method. +// Args: vText - (R) Text. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLogMediumFile::SetHeaderTxt(const CMIUtilString &vText) { + m_fileHeaderTxt = vText; + + return MIstatus::success; +} + +//++ +// Details: Retrieve the file current carriage line return characters used. +// Type: Method. +// Args: None. +// Return: CMIUtilString & - Text. +// Throws: None. +//-- +const CMIUtilString &CMICmnLogMediumFile::GetLineReturn() const { + return m_file.GetLineReturn(); +} + +//++ +// Details: Set the directory to place the log file. +// Type: Method. +// Args: vPath - (R) Path to log. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnLogMediumFile::SetDirectory(const CMIUtilString &vPath) { + m_strMediumFileDirectory = vPath; + + return FileFormFileNamePath(); +} diff --git a/src/MICmnLogMediumFile.h b/src/MICmnLogMediumFile.h new file mode 100644 index 00000000000..85b0a9d4f58 --- /dev/null +++ b/src/MICmnLogMediumFile.h @@ -0,0 +1,84 @@ +//===-- MICmnLogMediumFile.h ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmnBase.h" +#include "MICmnLog.h" +#include "MIUtilDateTimeStd.h" +#include "MIUtilFileStd.h" +#include "MIUtilString.h" + +//++ +//============================================================================ +// Details: MI common code implementation class. Logs application fn +// trace/message/ +// error messages to a file. Used as part of the CMICmnLog Logger +// system. When instantiated *this object is register with the Logger +// which the Logger when given data to write to registered medium comes +// *this medium. +// Singleton class. +//-- +class CMICmnLogMediumFile : public CMICmnBase, public CMICmnLog::IMedium { + // Statics: +public: + static CMICmnLogMediumFile &Instance(); + + // Methods: +public: + bool SetHeaderTxt(const CMIUtilString &vText); + bool SetVerbosity(const MIuint veType); + MIuint GetVerbosity() const; + const CMIUtilString &GetFileName() const; + const CMIUtilString &GetFileNamePath() const; + bool IsOk() const; + bool IsFileExist() const; + const CMIUtilString &GetLineReturn() const; + bool SetDirectory(const CMIUtilString &vPath); + + // Overridden: +public: + // From CMICmnBase + /* dtor */ ~CMICmnLogMediumFile() override; + // From CMICmnLog::IMedium + bool Initialize() override; + const CMIUtilString &GetName() const override; + bool Write(const CMIUtilString &vData, + const CMICmnLog::ELogVerbosity veType) override; + const CMIUtilString &GetError() const override; + bool Shutdown() override; + + // Methods: +private: + /* ctor */ CMICmnLogMediumFile(); + /* ctor */ CMICmnLogMediumFile(const CMICmnLogMediumFile &); + void operator=(const CMICmnLogMediumFile &); + + bool FileWriteEnglish(const CMIUtilString &vData); + bool FileFormFileNamePath(); + CMIUtilString MassagedData(const CMIUtilString &vData, + const CMICmnLog::ELogVerbosity veType); + bool FileWriteHeader(); + char ConvertLogVerbosityTypeToId(const CMICmnLog::ELogVerbosity veType) const; + CMIUtilString ConvertCr(const CMIUtilString &vData) const; + + // Attributes: +private: + const CMIUtilString m_constThisMediumName; + const CMIUtilString m_constMediumFileNameFormat; + // + CMIUtilString m_strMediumFileName; + CMIUtilString m_strMediumFileDirectory; + CMIUtilString m_fileNamePath; + MIuint m_eVerbosityType; + CMIUtilString m_strDate; + CMIUtilString m_fileHeaderTxt; + CMIUtilFileStd m_file; + CMIUtilDateTimeStd m_dateTime; +}; diff --git a/src/MICmnMIOutOfBandRecord.cpp b/src/MICmnMIOutOfBandRecord.cpp new file mode 100644 index 00000000000..59856a6f165 --- /dev/null +++ b/src/MICmnMIOutOfBandRecord.cpp @@ -0,0 +1,202 @@ +//===-- MICmnMIOutOfBandRecord.cpp ------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Third Party Headers: +#include + +// In-house headers: +#include "MICmnMIOutOfBandRecord.h" +#include "MICmnResources.h" + +// Instantiations: +static const char * +MapOutOfBandToText(CMICmnMIOutOfBandRecord::OutOfBand_e veType) { + switch (veType) { + case CMICmnMIOutOfBandRecord::eOutOfBand_Running: + return "running"; + case CMICmnMIOutOfBandRecord::eOutOfBand_Stopped: + return "stopped"; + case CMICmnMIOutOfBandRecord::eOutOfBand_BreakPointCreated: + return "breakpoint-created"; + case CMICmnMIOutOfBandRecord::eOutOfBand_BreakPointModified: + return "breakpoint-modified"; + case CMICmnMIOutOfBandRecord::eOutOfBand_Thread: + return ""; // "" Meant to be empty + case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupAdded: + return "thread-group-added"; + case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupExited: + return "thread-group-exited"; + case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupRemoved: + return "thread-group-removed"; + case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupStarted: + return "thread-group-started"; + case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadCreated: + return "thread-created"; + case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadExited: + return "thread-exited"; + case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadSelected: + return "thread-selected"; + case CMICmnMIOutOfBandRecord::eOutOfBand_TargetModuleLoaded: + return "library-loaded"; + case CMICmnMIOutOfBandRecord::eOutOfBand_TargetModuleUnloaded: + return "library-unloaded"; + case CMICmnMIOutOfBandRecord::eOutOfBand_TargetStreamOutput: + return ""; + case CMICmnMIOutOfBandRecord::eOutOfBand_ConsoleStreamOutput: + return ""; + case CMICmnMIOutOfBandRecord::eOutOfBand_LogStreamOutput: + return ""; + } + assert(false && "unknown CMICmnMIOutofBandRecord::OutOfBand_e"); + return nullptr; +} + +static const char * +MapOutOfBandToToken(CMICmnMIOutOfBandRecord::OutOfBand_e veType) { + switch (veType) { + case CMICmnMIOutOfBandRecord::eOutOfBand_Running: + return "*"; + case CMICmnMIOutOfBandRecord::eOutOfBand_Stopped: + return "*"; + case CMICmnMIOutOfBandRecord::eOutOfBand_BreakPointCreated: + return "="; + case CMICmnMIOutOfBandRecord::eOutOfBand_BreakPointModified: + return "="; + case CMICmnMIOutOfBandRecord::eOutOfBand_Thread: + return "@"; + case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupAdded: + return "="; + case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupExited: + return "="; + case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupRemoved: + return "="; + case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadGroupStarted: + return "="; + case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadCreated: + return "="; + case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadExited: + return "="; + case CMICmnMIOutOfBandRecord::eOutOfBand_ThreadSelected: + return "="; + case CMICmnMIOutOfBandRecord::eOutOfBand_TargetModuleLoaded: + return "="; + case CMICmnMIOutOfBandRecord::eOutOfBand_TargetModuleUnloaded: + return "="; + case CMICmnMIOutOfBandRecord::eOutOfBand_TargetStreamOutput: + return "@"; + case CMICmnMIOutOfBandRecord::eOutOfBand_ConsoleStreamOutput: + return "~"; + case CMICmnMIOutOfBandRecord::eOutOfBand_LogStreamOutput: + return "&"; + } + assert(false && "unknown CMICmnMIOutofBandRecord::OutOfBand_e"); + return nullptr; +} + +//++ +// Details: Build the Out-of-band record's mandatory data part. The part up to +// the first +// (additional) result i.e. async-record ==> "*" type. +// Args: veType - (R) A MI Out-of-Band enumeration. +// Return: CMIUtilString - The async record text. +// Throws: None. +//-- +static CMIUtilString +BuildAsyncRecord(CMICmnMIOutOfBandRecord::OutOfBand_e veType) { + auto Token = MapOutOfBandToToken(veType); + auto Text = MapOutOfBandToText(veType); + return CMIUtilString::Format("%s%s", CMIUtilString::WithNullAsEmpty(Token), + CMIUtilString::WithNullAsEmpty(Text)); +} + +//++ +// Details: CMICmnMIOutOfBandRecord constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnMIOutOfBandRecord::CMICmnMIOutOfBandRecord() + : m_strAsyncRecord(MIRSRC(IDS_CMD_ERR_EVENT_HANDLED_BUT_NO_ACTION)) {} + +//++ +// Details: CMICmnMIOutOfBandRecord constructor. +// Type: Method. +// Args: veType - (R) A MI Out-of-Bound enumeration. +// Return: None. +// Throws: None. +//-- +CMICmnMIOutOfBandRecord::CMICmnMIOutOfBandRecord(OutOfBand_e veType) + : m_strAsyncRecord(BuildAsyncRecord(veType)) {} + +//++ +// Details: CMICmnMIOutOfBandRecord constructor. +// Type: Method. +// Args: veType - (R) A MI Out-of-Bound enumeration. +// vConst - (R) A MI const object. +// Return: None. +// Throws: None. +//-- +CMICmnMIOutOfBandRecord::CMICmnMIOutOfBandRecord( + OutOfBand_e veType, const CMICmnMIValueConst &vConst) + : m_strAsyncRecord(BuildAsyncRecord(veType)) { + m_strAsyncRecord += vConst.GetString(); +} + +//++ +// Details: CMICmnMIOutOfBandRecord constructor. +// Type: Method. +// Args: veType - (R) A MI Out-of-Bound enumeration. +// vResult - (R) A MI result object. +// Return: None. +// Throws: None. +//-- +CMICmnMIOutOfBandRecord::CMICmnMIOutOfBandRecord( + OutOfBand_e veType, const CMICmnMIValueResult &vResult) + : m_strAsyncRecord(BuildAsyncRecord(veType)) { + Add(vResult); +} + +//++ +// Details: CMICmnMIOutOfBandRecord destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnMIOutOfBandRecord::~CMICmnMIOutOfBandRecord() {} + +//++ +// Details: Return the MI Out-of-band record as a string. The string is a direct +// result of +// work done on *this Out-of-band record so if not enough data is added +// then it is +// possible to return a malformed Out-of-band record. If nothing has +// been set or +// added to *this MI Out-of-band record object then text "" +// will be returned. +// Type: Method. +// Args: None. +// Return: CMIUtilString & - MI output text. +// Throws: None. +//-- +const CMIUtilString &CMICmnMIOutOfBandRecord::GetString() const { + return m_strAsyncRecord; +} + +//++ +// Details: Add to *this Out-of-band record additional information. +// Type: Method. +// Args: vResult - (R) A MI result object. +// Return: None. +// Throws: None. +//-- +void CMICmnMIOutOfBandRecord::Add(const CMICmnMIValueResult &vResult) { + m_strAsyncRecord += ","; + m_strAsyncRecord += vResult.GetString(); +} diff --git a/src/MICmnMIOutOfBandRecord.h b/src/MICmnMIOutOfBandRecord.h new file mode 100644 index 00000000000..ab1faab8bc4 --- /dev/null +++ b/src/MICmnMIOutOfBandRecord.h @@ -0,0 +1,93 @@ +//===-- MICmnMIOutOfBandRecord.h --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmnBase.h" +#include "MICmnMIValueConst.h" +#include "MICmnMIValueResult.h" +#include "MIUtilString.h" + +//++ +//============================================================================ +// Details: MI common code MI Out-of-band (Async) Record class. A class that +// encapsulates +// MI result record data and the forming/format of data added to it. +// Out-of-band records are used to notify the GDB/MI client of +// additional +// changes that have occurred. Those changes can either be a +// consequence +// of GDB/MI (e.g., a breakpoint modified) or a result of target +// activity +// (e.g., target stopped). +// The syntax is as follows: +// "*" type ( "," result )* +// type ==> running | stopped +// +// The Out-of-band record can be retrieve at any time *this object is +// instantiated so unless work is done on *this Out-of-band record then +// it is +// possible to return a malformed Out-of-band record. If nothing has +// been set +// or added to *this MI Out-of-band record object then text "" +// will +// be returned. +// +// More information see: +// http://ftp.gnu.org/old-gnu/Manuals/gdb-5.1.1/html_chapter/gdb_22.html// +//-- +class CMICmnMIOutOfBandRecord : public CMICmnBase { + // Enumerations: +public: + //++ + // Details: Enumeration of the type of Out-of-band for *this Out-of-band + // record + //-- + enum OutOfBand_e { + eOutOfBand_Running = 0, + eOutOfBand_Stopped, + eOutOfBand_BreakPointCreated, + eOutOfBand_BreakPointModified, + eOutOfBand_Thread, + eOutOfBand_ThreadGroupAdded, + eOutOfBand_ThreadGroupExited, + eOutOfBand_ThreadGroupRemoved, + eOutOfBand_ThreadGroupStarted, + eOutOfBand_ThreadCreated, + eOutOfBand_ThreadExited, + eOutOfBand_ThreadSelected, + eOutOfBand_TargetModuleLoaded, + eOutOfBand_TargetModuleUnloaded, + eOutOfBand_TargetStreamOutput, + eOutOfBand_ConsoleStreamOutput, + eOutOfBand_LogStreamOutput + }; + + // Methods: +public: + /* ctor */ CMICmnMIOutOfBandRecord(); + /* ctor */ CMICmnMIOutOfBandRecord(OutOfBand_e veType); + /* ctor */ CMICmnMIOutOfBandRecord(OutOfBand_e veType, + const CMICmnMIValueConst &vConst); + /* ctor */ CMICmnMIOutOfBandRecord(OutOfBand_e veType, + const CMICmnMIValueResult &vResult); + // + const CMIUtilString &GetString() const; + void Add(const CMICmnMIValueResult &vResult); + + // Overridden: +public: + // From CMICmnBase + /* dtor */ ~CMICmnMIOutOfBandRecord() override; + + // Attributes: +private: + CMIUtilString + m_strAsyncRecord; // Holds the text version of the result record to date +}; diff --git a/src/MICmnMIResultRecord.cpp b/src/MICmnMIResultRecord.cpp new file mode 100644 index 00000000000..93bb5a60ec3 --- /dev/null +++ b/src/MICmnMIResultRecord.cpp @@ -0,0 +1,133 @@ +//===-- MICmnMIResultRecord.cpp ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Third Party Headers: +#include + +// In-house headers: +#include "MICmnMIResultRecord.h" +#include "MICmnResources.h" + +//++ +// Details: Map a result class to the corresponding string. +// Args: veType - (R) A MI result class enumeration. +// Return: const char* - The string corresponding to the result class. +// Throws: None. +//-- +static const char * +MapResultClassToResultClassText(CMICmnMIResultRecord::ResultClass_e veType) { + switch (veType) { + case CMICmnMIResultRecord::eResultClass_Done: + return "done"; + case CMICmnMIResultRecord::eResultClass_Running: + return "running"; + case CMICmnMIResultRecord::eResultClass_Connected: + return "connected"; + case CMICmnMIResultRecord::eResultClass_Error: + return "error"; + case CMICmnMIResultRecord::eResultClass_Exit: + return "exit"; + } + assert(false && "unknown CMICmnMIResultRecord::ResultClass_e"); + return nullptr; +} + +//++ +// Details: Build the result record's mandatory data part. The part up to the +// first +// (additional) result i.e. result-record ==> [ token ] "^" +// result-class. +// Args: vrToken - (R) The command's transaction ID or token. +// veType - (R) A MI result class enumeration. +// Return: CMIUtilString & - MI result record mandatory data +// Throws: None. +//-- +static const CMIUtilString +BuildResultRecord(const CMIUtilString &vrToken, + CMICmnMIResultRecord::ResultClass_e veType) { + const char *pStrResultRecord = MapResultClassToResultClassText(veType); + return CMIUtilString::Format("%s^%s", vrToken.c_str(), + CMIUtilString::WithNullAsEmpty(pStrResultRecord)); +} + +//++ +// Details: CMICmnMIResultRecord constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnMIResultRecord::CMICmnMIResultRecord() + : m_strResultRecord(MIRSRC(IDS_CMD_ERR_CMD_RUN_BUT_NO_ACTION)) {} + +//++ +// Details: CMICmnMIResultRecord constructor. +// Type: Method. +// Args: vrToken - (R) The command's transaction ID or token. +// veType - (R) A MI result class enumeration. +// Return: None. +// Throws: None. +//-- +CMICmnMIResultRecord::CMICmnMIResultRecord(const CMIUtilString &vrToken, + ResultClass_e veType) + : m_strResultRecord(BuildResultRecord(vrToken, veType)) {} + +//++ +// Details: CMICmnMIResultRecord constructor. +// Type: Method. +// Args: vrToken - (R) The command's transaction ID or token. +// veType - (R) A MI result class enumeration. +// vMIResult - (R) A MI result object. +// Return: None. +// Throws: None. +//-- +CMICmnMIResultRecord::CMICmnMIResultRecord(const CMIUtilString &vrToken, + ResultClass_e veType, + const CMICmnMIValueResult &vValue) + : m_strResultRecord(BuildResultRecord(vrToken, veType)) { + Add(vValue); +} + +//++ +// Details: CMICmnMIResultRecord destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnMIResultRecord::~CMICmnMIResultRecord() {} + +//++ +// Details: Return the MI result record as a string. The string is a direct +// result of +// work done on *this result record so if not enough data is added then +// it is +// possible to return a malformed result record. If nothing has been +// set or +// added to *this MI result record object then text "" will be +// returned. +// Type: Method. +// Args: None. +// Return: CMIUtilString & - MI output text. +// Throws: None. +//-- +const CMIUtilString &CMICmnMIResultRecord::GetString() const { + return m_strResultRecord; +} + +//++ +// Details: Add to *this result record additional information. +// Type: Method. +// Args: vMIValue - (R) A MI value derived object. +// Return: None. +// Throws: None. +//-- +void CMICmnMIResultRecord::Add(const CMICmnMIValue &vMIValue) { + m_strResultRecord += ","; + m_strResultRecord += vMIValue.GetString(); +} diff --git a/src/MICmnMIResultRecord.h b/src/MICmnMIResultRecord.h new file mode 100644 index 00000000000..ae57007257c --- /dev/null +++ b/src/MICmnMIResultRecord.h @@ -0,0 +1,79 @@ +//===-- MICmnMIResultRecord.h -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmnBase.h" +#include "MICmnMIValueResult.h" +#include "MIUtilString.h" + +//++ +//============================================================================ +// Details: MI common code MI Result Record class. A class that encapsulates +// MI result record data and the forming/format of data added to it. +// The syntax is as follows: +// result-record ==> [ token ] "^" result-class ( "," result )* nl +// token = any sequence of digits +// * = 0 to many +// nl = CR | CR_LF +// result-class ==> "done" | "running" | "connected" | "error" | "exit" +// result ==> variable "=" value +// value ==> const | tuple | list +// const ==> c-string (7 bit iso c string content) i.e. "all" inc +// quotes +// tuple ==> "{}" | "{" result ( "," result )* "}" +// list ==> "[]" | "[" value ( "," value )* "]" | "[" result ( "," +// result )* "]" +// +// The result record can be retrieve at any time *this object is +// instantiated so unless work is done on *this result record then it +// is +// possible to return a malformed result record. If nothing has been +// set +// or added to *this MI result record object then text "" will +// be returned. +// More information see: +// http://ftp.gnu.org/old-gnu/Manuals/gdb-5.1.1/html_chapter/gdb_22.html +//-- +class CMICmnMIResultRecord : public CMICmnBase { + // Enumerations: +public: + //++ + // Details: Enumeration of the result class for *this result record + //-- + enum ResultClass_e { + eResultClass_Done = 0, + eResultClass_Running, + eResultClass_Connected, + eResultClass_Error, + eResultClass_Exit + }; + + // Methods: +public: + /* ctor */ CMICmnMIResultRecord(); + /* ctor */ CMICmnMIResultRecord(const CMIUtilString &vrToken, + ResultClass_e veType); + /* ctor */ CMICmnMIResultRecord(const CMIUtilString &vrToken, + ResultClass_e veType, + const CMICmnMIValueResult &vValue); + // + const CMIUtilString &GetString() const; + void Add(const CMICmnMIValue &vMIValue); + + // Overridden: +public: + // From CMICmnBase + /* dtor */ ~CMICmnMIResultRecord() override; + + // Attributes: +private: + CMIUtilString + m_strResultRecord; // Holds the text version of the result record to date +}; diff --git a/src/MICmnMIValue.cpp b/src/MICmnMIValue.cpp new file mode 100644 index 00000000000..05ed71e584e --- /dev/null +++ b/src/MICmnMIValue.cpp @@ -0,0 +1,43 @@ +//===-- MICmnMIValue.cpp ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmnMIValue.h" +#include "MICmnResources.h" + +//++ +// Details: CMICmnMIValue constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnMIValue::CMICmnMIValue() + : m_strValue(MIRSRC(IDS_WORD_INVALIDBRKTS)), m_bJustConstructed(true) {} + +//++ +// Details: CMICmnMIValue destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnMIValue::~CMICmnMIValue() {} + +//++ +// Details: Return the MI value as a string. The string is a direct result of +// work done on *this value so if not enough data is added then it is +// possible to return a malformed value. If nothing has been set or +// added to *this MI value object then text "" will be +// returned. +// Type: Method. +// Args: None. +// Return: CMIUtilString & - MI output text. +// Throws: None. +//-- +const CMIUtilString &CMICmnMIValue::GetString() const { return m_strValue; } diff --git a/src/MICmnMIValue.h b/src/MICmnMIValue.h new file mode 100644 index 00000000000..8f10c679361 --- /dev/null +++ b/src/MICmnMIValue.h @@ -0,0 +1,50 @@ +//===-- MICmnMIValue.h ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmnBase.h" + +//++ +//============================================================================ +// Details: MI common code MI Result class. Part of the CMICmnMIValueRecord +// set of objects. +// The syntax is as follows: +// result-record ==> [ token ] "^" result-class ( "," result )* nl +// token = any sequence of digits +// * = 0 to many +// nl = CR | CR_LF +// result-class ==> "done" | "running" | "connected" | "error" | "exit" +// result ==> variable "=" value +// value ==> const | tuple | list +// const ==> c-string (7 bit iso c string content) +// tuple ==> "{}" | "{" result ( "," result )* "}" +// list ==> "[]" | "[" value ( "," value )* "]" | "[" result ( "," +// result )* "]" +// More information see: +// http://ftp.gnu.org/old-gnu/Manuals/gdb-5.1.1/html_chapter/gdb_22.html +//-- +class CMICmnMIValue : public CMICmnBase { + // Methods: +public: + /* ctor */ CMICmnMIValue(); + // + const CMIUtilString &GetString() const; + + // Overridden: +public: + // From CMICmnBase + /* dtor */ ~CMICmnMIValue() override; + + // Attributes: +protected: + CMIUtilString m_strValue; + bool m_bJustConstructed; // True = *this just constructed with no value, false + // = *this has had value added to it +}; diff --git a/src/MICmnMIValueConst.cpp b/src/MICmnMIValueConst.cpp new file mode 100644 index 00000000000..1cdbc6487b2 --- /dev/null +++ b/src/MICmnMIValueConst.cpp @@ -0,0 +1,77 @@ +//===-- MICmnMIValueConst.cpp -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmnMIValueConst.h" + +// Instantiations: +const CMIUtilString CMICmnMIValueConst::ms_constStrDblQuote("\""); + +//++ +// Details: CMICmnMIValueConst constructor. +// Type: Method. +// Args: vString - (R) MI Const c-string value. +// Return: None. +// Throws: None. +//-- +CMICmnMIValueConst::CMICmnMIValueConst(const CMIUtilString &vString) + : m_strPartConst(vString), m_bNoQuotes(false) { + BuildConst(); +} + +//++ +// Details: CMICmnMIValueConst constructor. +// Type: Method. +// Args: vString - (R) MI Const c-string value. +// vbNoQuotes - (R) True = return string not surrounded with quotes, +// false = use quotes. +// Return: None. +// Throws: None. +//-- +CMICmnMIValueConst::CMICmnMIValueConst(const CMIUtilString &vString, + const bool vbNoQuotes) + : m_strPartConst(vString), m_bNoQuotes(vbNoQuotes) { + BuildConst(); +} + +//++ +// Details: CMICmnMIValueConst destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnMIValueConst::~CMICmnMIValueConst() {} + +//++ +// Details: Build the Value Const data. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnMIValueConst::BuildConst() { + if (m_strPartConst.length() != 0) { + const CMIUtilString strValue(m_strPartConst.StripCREndOfLine()); + if (m_bNoQuotes) { + m_strValue = strValue; + } else { + const char *pFormat = "%s%s%s"; + m_strValue = + CMIUtilString::Format(pFormat, ms_constStrDblQuote.c_str(), + strValue.c_str(), ms_constStrDblQuote.c_str()); + } + } else { + const char *pFormat = "%s%s"; + m_strValue = CMIUtilString::Format(pFormat, ms_constStrDblQuote.c_str(), + ms_constStrDblQuote.c_str()); + } + + return MIstatus::success; +} diff --git a/src/MICmnMIValueConst.h b/src/MICmnMIValueConst.h new file mode 100644 index 00000000000..4bac0864c9c --- /dev/null +++ b/src/MICmnMIValueConst.h @@ -0,0 +1,59 @@ +//===-- MICmnMIValueConst.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmnMIValue.h" + +//++ +//============================================================================ +// Details: MI common code MI Result class. Part of the CMICmnMIValueConstRecord +// set of objects. +// The syntax is as follows: +// result-record ==> [ token ] "^" result-class ( "," result )* nl +// token = any sequence of digits +// * = 0 to many +// nl = CR | CR_LF +// result-class ==> "done" | "running" | "connected" | "error" | "exit" +// result ==> variable "=" value +// value ==> const | tuple | list +// const ==> c-string (7 bit iso c string content) +// tuple ==> "{}" | "{" result ( "," result )* "}" +// list ==> "[]" | "[" value ( "," value )* "]" | "[" result ( "," +// result )* "]" +// More information see: +// http://ftp.gnu.org/old-gnu/Manuals/gdb-5.1.1/html_chapter/gdb_22.html +// +// The text formed in *this Result class is stripped of any '\n' +// characters. +//-- +class CMICmnMIValueConst : public CMICmnMIValue { + // Methods: +public: + /* ctor */ CMICmnMIValueConst(const CMIUtilString &vString); + /* ctor */ CMICmnMIValueConst(const CMIUtilString &vString, + const bool vbNoQuotes); + + // Overridden: +public: + // From CMICmnBase + /* dtor */ ~CMICmnMIValueConst() override; + + // Methods: +private: + bool BuildConst(); + + // Attributes: +private: + static const CMIUtilString ms_constStrDblQuote; + // + CMIUtilString m_strPartConst; + bool m_bNoQuotes; // True = return string not surrounded with quotes, false = + // use quotes +}; diff --git a/src/MICmnMIValueList.cpp b/src/MICmnMIValueList.cpp new file mode 100644 index 00000000000..038d3ae36f0 --- /dev/null +++ b/src/MICmnMIValueList.cpp @@ -0,0 +1,177 @@ +//===-- MICmnMIValueList.cpp ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmnMIValueList.h" +#include "MICmnResources.h" + +//++ +// Details: CMICmnMIValueList constructor. +// Type: Method. +// Args: vbValueTypeList - (R) True = yes value type list, false = result +// type list. +// Return: None. +// Throws: None. +//-- +CMICmnMIValueList::CMICmnMIValueList(const bool vbValueTypeList) { + m_strValue = "[]"; +} + +//++ +// Details: CMICmnMIValueList constructor. +// Construct a results only list. +// return MIstatus::failure. +// Type: Method. +// Args: vResult - (R) MI result object. +// Return: None. +// Throws: None. +//-- +CMICmnMIValueList::CMICmnMIValueList(const CMICmnMIValueResult &vResult) { + m_strValue = vResult.GetString(); + BuildList(); + m_bJustConstructed = false; +} + +//++ +// Details: CMICmnMIValueList constructor. +// Construct a value only list. +// Type: Method. +// Args: vValue - (R) MI value object. +// Return: None. +// Throws: None. +//-- +CMICmnMIValueList::CMICmnMIValueList(const CMICmnMIValue &vValue) { + m_strValue = vValue.GetString(); + BuildList(); + m_bJustConstructed = false; +} + +//++ +// Details: CMICmnMIValueList destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnMIValueList::~CMICmnMIValueList() {} + +//++ +// Details: Build the result value's mandatory data part, one tuple +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMICmnMIValueList::BuildList() { + const char *pFormat = "[%s]"; + m_strValue = CMIUtilString::Format(pFormat, m_strValue.c_str()); +} + +//++ +// Details: Add another MI result object to the value list's of list is +// results. +// Only result objects can be added to a list of result otherwise this +// function +// will return MIstatus::failure. +// Type: Method. +// Args: vResult - (R) The MI result object. +// Return: None. +// Throws: None. +//-- +void CMICmnMIValueList::Add(const CMICmnMIValueResult &vResult) { + BuildList(vResult); +} + +//++ +// Details: Add another MI value object to the value list's of list is values. +// Only values objects can be added to a list of values otherwise this +// function +// will return MIstatus::failure. +// Type: Method. +// Args: vValue - (R) The MI value object. +// Return: None. +// Throws: None. +//-- +void CMICmnMIValueList::Add(const CMICmnMIValue &vValue) { BuildList(vValue); } + +//++ +// Details: Add another MI result object to the value list's of list is +// results. +// Only result objects can be added to a list of result otherwise this +// function +// will return MIstatus::failure. +// Type: Method. +// Args: vResult - (R) The MI result object. +// Return: None. +// Throws: None. +//-- +void CMICmnMIValueList::BuildList(const CMICmnMIValueResult &vResult) { + // Clear out the default "" text + if (m_bJustConstructed) { + m_bJustConstructed = false; + m_strValue = vResult.GetString(); + BuildList(); + return; + } + + const CMIUtilString data(ExtractContentNoBrackets()); + const char *pFormat = "[%s,%s]"; + m_strValue = + CMIUtilString::Format(pFormat, data.c_str(), vResult.GetString().c_str()); +} + +//++ +// Details: Add another MI value object to the value list's of list is values. +// Only values objects can be added to a list of values otherwise this +// function +// will return MIstatus::failure. +// Type: Method. +// Args: vValue - (R) The MI value object. +// Return: None. +// Throws: None. +//-- +void CMICmnMIValueList::BuildList(const CMICmnMIValue &vValue) { + // Clear out the default "" text + if (m_bJustConstructed) { + m_bJustConstructed = false; + m_strValue = vValue.GetString(); + BuildList(); + return; + } + + // Remove already present '[' and ']' from the start and end + m_strValue = m_strValue.Trim(); + size_t len = m_strValue.size(); + if ((len > 1) && (m_strValue[0] == '[') && (m_strValue[len - 1] == ']')) + m_strValue = m_strValue.substr(1, len - 2); + const char *pFormat = "[%s,%s]"; + m_strValue = CMIUtilString::Format(pFormat, m_strValue.c_str(), + vValue.GetString().c_str()); +} + +//++ +// Details: Retrieve the contents of *this value object but without the outer +// most +// brackets. +// Type: Method. +// Args: None. +// Return: CMIUtilString - Data within the object. +// Throws: None. +//-- +CMIUtilString CMICmnMIValueList::ExtractContentNoBrackets() const { + CMIUtilString data(m_strValue); + + if (data[0] == '[') { + data = data.substr(1, data.length() - 1); + } + if (data[data.size() - 1] == ']') { + data = data.substr(0, data.length() - 1); + } + + return data; +} diff --git a/src/MICmnMIValueList.h b/src/MICmnMIValueList.h new file mode 100644 index 00000000000..76062d0b28d --- /dev/null +++ b/src/MICmnMIValueList.h @@ -0,0 +1,55 @@ +//===-- MICmnMIValueList.h --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmnMIValue.h" +#include "MICmnMIValueResult.h" + +//++ +//============================================================================ +// Details: MI common code MI Result class. Part of the CMICmnMIValueListRecord +// set of objects. +// The syntax is as follows: +// result-record ==> [ token ] "^" result-class ( "," result )* nl +// token = any sequence of digits +// * = 0 to many +// nl = CR | CR_LF +// result-class ==> "done" | "running" | "connected" | "error" | "exit" +// result ==> variable "=" value +// value ==> const | tuple | list +// const ==> c-string (7 bit iso c string content) +// tuple ==> "{}" | "{" result ( "," result )* "}" +// list ==> "[]" | "[" value ( "," value )* "]" | "[" result ( "," +// result )* "]" +// More information see: +// http://ftp.gnu.org/old-gnu/Manuals/gdb-5.1.1/html_chapter/gdb_22.html +//-- +class CMICmnMIValueList : public CMICmnMIValue { + // Methods: +public: + /* ctor */ CMICmnMIValueList(const bool vbValueTypeList); + /* ctor */ CMICmnMIValueList(const CMICmnMIValueResult &vResult); + /* ctor */ CMICmnMIValueList(const CMICmnMIValue &vValue); + // + void Add(const CMICmnMIValueResult &vResult); + void Add(const CMICmnMIValue &vValue); + CMIUtilString ExtractContentNoBrackets() const; + + // Overridden: +public: + // From CMICmnBase + /* dtor */ ~CMICmnMIValueList() override; + + // Methods: +private: + void BuildList(); + void BuildList(const CMICmnMIValueResult &vResult); + void BuildList(const CMICmnMIValue &vResult); +}; diff --git a/src/MICmnMIValueResult.cpp b/src/MICmnMIValueResult.cpp new file mode 100644 index 00000000000..5ef60741342 --- /dev/null +++ b/src/MICmnMIValueResult.cpp @@ -0,0 +1,116 @@ +//===-- MICmnMIValueResult.cpp ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmnMIValueResult.h" +#include "MICmnResources.h" + +// Instantiations: +const CMIUtilString CMICmnMIValueResult::ms_constStrEqual("="); + +//++ +// Details: CMICmnMIValueResult constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnMIValueResult::CMICmnMIValueResult() : m_bEmptyConstruction(true) {} + +//++ +// Details: CMICmnMIValueResult constructor. +// Type: Method. +// Args: vrVariable - (R) MI value's name. +// vrValue - (R) The MI value. +// Return: None. +// Throws: None. +//-- +CMICmnMIValueResult::CMICmnMIValueResult(const CMIUtilString &vrVariable, + const CMICmnMIValue &vrValue) + : m_strPartVariable(vrVariable), m_partMIValue(vrValue), + m_bEmptyConstruction(false), m_bUseSpacing(false) { + BuildResult(); +} + +//++ +// Details: CMICmnMIValueResult constructor. +// Type: Method. +// Args: vrVariable - (R) MI value's name. +// vrValue - (R) The MI value. +// vbUseSpacing - (R) True = put space separators into the string, +// false = no spaces used. +// Return: None. +// Throws: None. +//-- +CMICmnMIValueResult::CMICmnMIValueResult(const CMIUtilString &vrVariable, + const CMICmnMIValue &vrValue, + const bool vbUseSpacing) + : m_strPartVariable(vrVariable), m_partMIValue(vrValue), + m_bEmptyConstruction(false), m_bUseSpacing(vbUseSpacing) { + BuildResult(); +} + +//++ +// Details: CMICmnMIValueResult destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnMIValueResult::~CMICmnMIValueResult() {} + +//++ +// Details: Build the MI value result string. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMICmnMIValueResult::BuildResult() { + const char *pFormat = m_bUseSpacing ? "%s %s %s" : "%s%s%s"; + m_strValue = CMIUtilString::Format(pFormat, m_strPartVariable.c_str(), + ms_constStrEqual.c_str(), + m_partMIValue.GetString().c_str()); +} + +//++ +// Details: Build the MI value result string. +// Type: Method. +// Args: vrVariable - (R) MI value's name. +// vrValue - (R) The MI value. +// Return: None. +// Throws: None. +//-- +void CMICmnMIValueResult::BuildResult(const CMIUtilString &vVariable, + const CMICmnMIValue &vValue) { + const char *pFormat = m_bUseSpacing ? "%s, %s %s %s" : "%s,%s%s%s"; + m_strValue = CMIUtilString::Format( + pFormat, m_strValue.c_str(), vVariable.c_str(), ms_constStrEqual.c_str(), + vValue.GetString().c_str()); +} + +//++ +// Details: Append another MI value object to *this MI value result. +// Type: Method. +// Args: vrVariable - (R) MI value's name. +// vrValue - (R) The MI value. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +void CMICmnMIValueResult::Add(const CMIUtilString &vrVariable, + const CMICmnMIValue &vrValue) { + if (!m_bEmptyConstruction) + BuildResult(vrVariable, vrValue); + else { + m_bEmptyConstruction = false; + m_strPartVariable = vrVariable; + m_partMIValue = vrValue; + BuildResult(); + } +} diff --git a/src/MICmnMIValueResult.h b/src/MICmnMIValueResult.h new file mode 100644 index 00000000000..cfa101b86a5 --- /dev/null +++ b/src/MICmnMIValueResult.h @@ -0,0 +1,66 @@ +//===-- MICmnMIValueResult.h ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmnMIValue.h" + +//++ +//============================================================================ +// Details: MI common code MI Result class. Part of the +// CMICmnMIValueResultRecord +// set of objects. +// The syntax is as follows: +// result-record ==> [ token ] "^" result-class ( "," result )* nl +// token = any sequence of digits +// * = 0 to many +// nl = CR | CR_LF +// result-class ==> "done" | "running" | "connected" | "error" | "exit" +// result ==> variable "=" value +// value ==> const | tuple | list +// const ==> c-string (7 bit iso c string content) +// tuple ==> "{}" | "{" result ( "," result )* "}" +// list ==> "[]" | "[" value ( "," value )* "]" | "[" result ( "," +// result )* "]" +// More information see: +// http://ftp.gnu.org/old-gnu/Manuals/gdb-5.1.1/html_chapter/gdb_22.html +//-- +class CMICmnMIValueResult : public CMICmnMIValue { + // Methods: +public: + /* ctor */ CMICmnMIValueResult(); + /* ctor */ CMICmnMIValueResult(const CMIUtilString &vVariable, + const CMICmnMIValue &vValue); + /* ctor */ CMICmnMIValueResult(const CMIUtilString &vVariable, + const CMICmnMIValue &vValue, + const bool vbUseSpacing); + // + void Add(const CMIUtilString &vVariable, const CMICmnMIValue &vValue); + + // Overridden: +public: + // From CMICmnBase + /* dtor */ ~CMICmnMIValueResult() override; + + // Methods: +private: + void BuildResult(); + void BuildResult(const CMIUtilString &vVariable, const CMICmnMIValue &vValue); + + // Attributes: +private: + static const CMIUtilString ms_constStrEqual; + // + CMIUtilString m_strPartVariable; + CMICmnMIValue m_partMIValue; + bool m_bEmptyConstruction; // True = *this object used constructor with no + // parameters, false = constructor with parameters + bool m_bUseSpacing; // True = put space separators into the string, false = no + // spaces used +}; diff --git a/src/MICmnMIValueTuple.cpp b/src/MICmnMIValueTuple.cpp new file mode 100644 index 00000000000..2f05d219d4f --- /dev/null +++ b/src/MICmnMIValueTuple.cpp @@ -0,0 +1,194 @@ +//===-- MICmnMIValueTuple.cpp -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmnMIValueTuple.h" + +//++ +// Details: CMICmnMIValueTuple constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnMIValueTuple::CMICmnMIValueTuple() : m_bSpaceAfterComma(false) { + m_strValue = "{}"; +} + +//++ +// Details: CMICmnMIValueTuple constructor. +// Type: Method. +// Args: vResult - (R) MI result object. +// Return: None. +// Throws: None. +//-- +CMICmnMIValueTuple::CMICmnMIValueTuple(const CMICmnMIValueResult &vResult) + : m_bSpaceAfterComma(false) { + m_strValue = vResult.GetString(); + BuildTuple(); + m_bJustConstructed = false; +} + +//++ +// Details: CMICmnMIValueTuple constructor. +// Type: Method. +// Args: vResult - (R) MI result object. +// vbUseSpacing - (R) True = put space separators into the string, +// false = no spaces used. +// Return: None. +// Throws: None. +//-- +CMICmnMIValueTuple::CMICmnMIValueTuple(const CMICmnMIValueResult &vResult, + const bool vbUseSpacing) + : m_bSpaceAfterComma(vbUseSpacing) { + m_strValue = vResult.GetString(); + BuildTuple(); + m_bJustConstructed = false; +} + +//++ +// Details: CMICmnMIValueTuple destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnMIValueTuple::~CMICmnMIValueTuple() {} + +//++ +// Details: Build the result value's mandatory data part, one tuple +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMICmnMIValueTuple::BuildTuple() { + const char *pFormat = "{%s}"; + m_strValue = CMIUtilString::Format(pFormat, m_strValue.c_str()); +} + +//++ +// Details: Add another MI result object to the value's list of tuples. +// Type: Method. +// Args: vResult - (R) The MI result object. +// Return: None. +// Throws: None. +//-- +void CMICmnMIValueTuple::BuildTuple(const CMICmnMIValueResult &vResult) { + // Clear out the default "" text + if (m_bJustConstructed) { + m_bJustConstructed = false; + m_strValue = vResult.GetString(); + BuildTuple(); + return; + } + + if (m_strValue[0] == '{') { + m_strValue = m_strValue.substr(1, m_strValue.size() - 1); + } + if (m_strValue[m_strValue.size() - 1] == '}') { + m_strValue = m_strValue.substr(0, m_strValue.size() - 1); + } + + const char *pFormat = m_bSpaceAfterComma ? "{%s, %s}" : "{%s,%s}"; + m_strValue = CMIUtilString::Format(pFormat, m_strValue.c_str(), + vResult.GetString().c_str()); +} + +//++ +// Details: Add string value to the value's list of tuples. +// Type: Method. +// Args: vValue - (R) The string object. +// Return: None. +// Throws: None. +//-- +void CMICmnMIValueTuple::BuildTuple(const CMIUtilString &vValue) { + // Clear out the default "" text + if (m_bJustConstructed) { + m_bJustConstructed = false; + m_strValue = vValue; + BuildTuple(); + return; + } + + const CMIUtilString data(ExtractContentNoBrackets()); + const char *pFormat = m_bSpaceAfterComma ? "{%s, %s}" : "{%s,%s}"; + m_strValue = CMIUtilString::Format(pFormat, data.c_str(), vValue.c_str()); +} + +//++ +// Details: Add another MI value object to the value list's of list is values. +// Only values objects can be added to a list of values otherwise this +// function +// will return MIstatus::failure. +// Type: Method. +// Args: vValue - (R) The MI value object. +// Return: None. +// Throws: None. +//-- +void CMICmnMIValueTuple::Add(const CMICmnMIValueResult &vResult) { + BuildTuple(vResult); +} + +//++ +// Details: Add another MI value object to the value list's of list is values. +// Only values objects can be added to a list of values otherwise this +// function +// will return MIstatus::failure. +// Type: Method. +// Args: vValue - (R) The MI value object. +// vbUseSpacing - (R) True = put space separators into the string, +// false = no spaces used. +// Return: None. +// Throws: None. +//-- +void CMICmnMIValueTuple::Add(const CMICmnMIValueResult &vResult, + const bool vbUseSpacing) { + m_bSpaceAfterComma = vbUseSpacing; + BuildTuple(vResult); +} + +//++ +// Details: Add another MI value object to the value list's of list is values. +// Only values objects can be added to a list of values otherwise this +// function +// will return MIstatus::failure. +// Type: Method. +// Args: vValue - (R) The MI value object. +// vbUseSpacing - (R) True = put space separators into the string, +// false = no spaces used. +// Return: None. +// Throws: None. +//-- +void CMICmnMIValueTuple::Add(const CMICmnMIValueConst &vValue, + const bool vbUseSpacing) { + m_bSpaceAfterComma = vbUseSpacing; + BuildTuple(vValue.GetString()); +} + +//++ +// Details: Retrieve the contents of *this value object but without the outer +// most +// brackets. +// Type: Method. +// Args: None. +// Return: CMIUtilString - Data within the object. +// Throws: None. +//-- +CMIUtilString CMICmnMIValueTuple::ExtractContentNoBrackets() const { + CMIUtilString data(m_strValue); + + if (data[0] == '{') { + data = data.substr(1, data.length() - 1); + } + if (data[data.size() - 1] == '}') { + data = data.substr(0, data.length() - 1); + } + + return data; +} diff --git a/src/MICmnMIValueTuple.h b/src/MICmnMIValueTuple.h new file mode 100644 index 00000000000..4a04fd9214e --- /dev/null +++ b/src/MICmnMIValueTuple.h @@ -0,0 +1,63 @@ +//===-- MICmnMIValueTuple.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmnMIValue.h" +#include "MICmnMIValueConst.h" +#include "MICmnMIValueResult.h" + +//++ +//============================================================================ +// Details: MI common code MI Result class. Part of the CMICmnMIValueTupleRecord +// set of objects. +// The syntax is as follows: +// result-record ==> [ token ] "^" result-class ( "," result )* nl +// token = any sequence of digits +// * = 0 to many +// nl = CR | CR_LF +// result-class ==> "done" | "running" | "connected" | "error" | "exit" +// result ==> variable "=" value +// value ==> const | tuple | list +// const ==> c-string (7 bit iso c string content) +// tuple ==> "{}" | "{" result ( "," result )* "}" +// list ==> "[]" | "[" value ( "," value )* "]" | "[" result ( "," +// result )* "]" +// More information see: +// http://ftp.gnu.org/old-gnu/Manuals/gdb-5.1.1/html_chapter/gdb_22.html +//-- +class CMICmnMIValueTuple : public CMICmnMIValue { + // Methods: +public: + /* ctor */ CMICmnMIValueTuple(); + /* ctor */ CMICmnMIValueTuple(const CMICmnMIValueResult &vResult); + /* ctor */ CMICmnMIValueTuple(const CMICmnMIValueResult &vResult, + const bool vbUseSpacing); + // + void Add(const CMICmnMIValueResult &vResult); + void Add(const CMICmnMIValueResult &vResult, const bool vbUseSpacing); + void Add(const CMICmnMIValueConst &vValue, const bool vbUseSpacing); + CMIUtilString ExtractContentNoBrackets() const; + + // Overridden: +public: + // From CMICmnBase + /* dtor */ ~CMICmnMIValueTuple() override; + + // Methods: +private: + void BuildTuple(); + void BuildTuple(const CMICmnMIValueResult &vResult); + void BuildTuple(const CMIUtilString &vValue); + + // Attributes: +private: + bool m_bSpaceAfterComma; // True = put space separators into the string, false + // = no spaces used +}; diff --git a/src/MICmnResources.cpp b/src/MICmnResources.cpp new file mode 100644 index 00000000000..3925f0b01dc --- /dev/null +++ b/src/MICmnResources.cpp @@ -0,0 +1,619 @@ +//===-- MICmnResources.cpp --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Third party headers +#include "assert.h" +#include + +// In-house headers: +#include "MICmnResources.h" + +// Instantiations: +const CMICmnResources::SRsrcTextData + CMICmnResources::ms_pResourceId2TextData[] = { + {IDS_PROJNAME, + "LLDB Machine Interface Driver (MI) All rights reserved"}, + {IDS_MI_VERSION_DESCRIPTION_DEBUG, + "Version: 1.0.0.9 (Debug)"}, // See version history in MIDriverMain.cpp + {IDS_MI_VERSION_DESCRIPTION, "Version: 1.0.0.9"}, + {IDS_MI_APPNAME_SHORT, "MI"}, + {IDS_MI_APPNAME_LONG, "Machine Interface Driver"}, + {IDS_MI_APP_FILEPATHNAME, "Application: %s"}, + {IDS_MI_APP_ARGS, "Command line args: "}, + {IDE_MI_VERSION_GDB, "Version: GNU gdb (GDB) 7.4 \n(This is a MI stub " + "on top of LLDB and not GDB)\nAll rights " + "reserved.\n"}, // *** Eclipse needs this + // exactly!! + {IDS_UTIL_FILE_ERR_INVALID_PATHNAME, + "File Handler. Invalid file name path"}, + {IDS_UTIL_FILE_ERR_OPENING_FILE, "File Handler. Error %s opening '%s'"}, + {IDS_UTIL_FILE_ERR_OPENING_FILE_UNKNOWN, + "File Handler. Unknown error opening '%s'"}, + {IDE_UTIL_FILE_ERR_WRITING_FILE, "File Handler. Error %s writing '%s'"}, + {IDE_UTIL_FILE_ERR_WRITING_NOTOPEN, + "File Handler. File '%s' not open for write"}, + {IDS_RESOURCES_ERR_STRING_NOT_FOUND, + "Resources. String (%d) not found in resources"}, + {IDS_RESOURCES_ERR_STRING_TABLE_INVALID, + "Resources. String resource table is not set up"}, + {IDS_MI_CLIENT_MSG, "Client message: \"%s\""}, + {IDS_LOG_MSG_CREATION_DATE, "Creation date %s time %s%s"}, + {IDS_LOG_MSG_FILE_LOGGER_PATH, "File logger path: %s%s"}, + {IDS_LOG_MSG_VERSION, "Version: %s%s"}, + {IDS_LOG_ERR_FILE_LOGGER_DISABLED, + "Log. File logger temporarily disabled due to file error '%s'"}, + {IDS_LOG_MEDIUM_ERR_INIT, "Log. Medium '%s' initialise failed. %s"}, + {IDS_LOG_MEDIUM_ERR_WRITE_ANY, + "Log. Failed to write log data to any medium."}, + {IDS_LOG_MEDIUM_ERR_WRITE_MEDIUMFAIL, + "Log. One or mediums failed writing log data."}, + {IDS_MEDIUMFILE_NAME, "File"}, + {IDS_MEDIUMFILE_ERR_INVALID_PATH, ""}, + {IDS_MEDIUMFILE_ERR_FILE_HEADER, ""}, + {IDS_MEDIUMFILE_NAME_LOG, "File medium. %s"}, + {IDE_OS_ERR_UNKNOWN, "Unknown OS error"}, + {IDE_OS_ERR_RETRIEVING, "Unabled to retrieve OS error message"}, + {IDS_DRIVERMGR_DRIVER_ERR_INIT, + "Driver Manager. Driver '%s' (ID:'%s') initialise failed. %s"}, + {IDE_MEDIUMSTDERR_NAME, "Stderr"}, + {IDE_MEDIUMSTDOUT_NAME, "Stdout"}, + {IDE_MI_APP_DESCRIPTION, + "Description:\nThe Machine Interface Driver (MI Driver) is a stand " + "alone executable\nthat either be used via " + "a client i.e. Eclipse or directly from the command\nline. It " + "processes MI commands, actions those commands " + "using the internal\ndebugger then forms MI response formatted text " + "which is returned to the\nclient."}, + {IDE_MI_APP_INFORMATION, + "Information:\nCurrent limitations. The MI Driver currently only " + "handles remote target\ndebugging. Local " + "debugging has not been implemented. The MI Driver has\nbeen designed " + "primarily to be used with Eclipse Juno " + "and a custom plugin.\nThe custom plugin is not necessary to operate " + "the MI Driver."}, + {IDE_MI_APP_ARG_USAGE, "\nMI driver usage:\n\n\tlldb-mi [--longOption] " + "[-s hortOption] [executable]\n\n[] = optional " + "argument."}, + {IDE_MI_APP_ARG_HELP, "-h\n--help\n\tPrints out usage information for " + "the MI debugger. Exit the MI\n\tDriver " + "immediately."}, + {IDE_MI_APP_ARG_VERSION, "--version\n\tPrints out GNU (gdb) version " + "information. Exit the MI " + "Driver\n\timmediately."}, + {IDE_MI_APP_ARG_VERSION_LONG, "--versionLong\n\tPrints out MI Driver " + "version information. Exit the MI " + "Driver\n\timmediately."}, + {IDE_MI_APP_ARG_INTERPRETER, "--interpreter\n\t This option is kept " + "for backward compatibility. This " + "executable always run in MI mode"}, + {IDE_MI_APP_ARG_EXECUTEABLE, "--executable\n\tUse the MI Driver in MI " + "mode for the debugging the specified " + "executable."}, + {IDE_MI_APP_ARG_SOURCE, "-s \n--source \n\t" + "Tells the debugger to read in and execute the " + "lldb commands in the\n\t" + "given file, after any file provided on the " + "command line has been\n\tloaded."}, + {IDE_MI_APP_ARG_APP_LOG, "--log\n\tUse this argument to tell the MI " + "Driver to update it's log\n\tfile '%s'."}, + {IDE_MI_APP_ARG_APP_LOG_DIR, + "--log-dir\n\tUse this argument to specify the directory the MI " + "Driver\n\twill place the log file in, i.e --log-dir=/tmp."}, + {IDE_MI_APP_ARG_EXAMPLE, "Example MI command:\n\t3-info-gdb-mi-command " + "gdb-set\n\t3^done,command={exists=\"true\"}"}, + {IDE_MI_APP_ARG_EXECUTABLE, "executable (NOT IMPLEMENTED)\n\tThe file " + "path to the executable i.e. '\"C:\\My " + "Dev\\foo.exe\"'."}, + {IDE_MI_APP_ARG_SYNCHRONOUS, "--synchronous\n\tBlock until each command " + "has finished executing.\n\tUsed for testing only."}, + {IDS_STDIN_ERR_INVALID_PROMPT, + "Stdin. Invalid prompt description '%s'"}, + {IDS_STDIN_ERR_THREAD_CREATION_FAILED, + "Stdin. Thread creation failed '%s'"}, + {IDS_STDIN_ERR_THREAD_DELETE, "Stdin. Thread failed to delete '%s'"}, + {IDS_STDIN_ERR_CHKING_BYTE_AVAILABLE, + "Stdin. Peeking on stdin stream '%s'"}, + {IDS_STDIN_INPUT_CTRL_CHARS, + "Stdin. Receive characters not handled as a command: "}, + {IDS_CMD_QUIT_HELP, + "MI Driver Command: quit\n\tExit the MI Driver application."}, + {IDS_THREADMGR_ERR_THREAD_ID_INVALID, + "Thread Mgr. Thread ID '%s' is not valid"}, + {IDS_THREADMGR_ERR_THREAD_FAIL_CREATE, + "Thread Mgr: Failed to create thread '%s'"}, + {IDS_THREADMGR_ERR_THREAD_ID_NOT_FOUND, + "Thread Mgr: Thread with ID '%s' not found"}, + {IDS_THREADMGR_ERR_THREAD_STILL_ALIVE, "Thread Mgr: The thread(s) are " + "still alive at Thread Mgr " + "shutdown: %s"}, + {IDS_FALLTHRU_DRIVER_CMD_RECEIVED, + "Fall Thru Driver. Received command '%s'. Is was %shandled"}, + {IDS_CMDFACTORY_ERR_INVALID_CMD_NAME, + "Command factory. MI command name '%s' is invalid"}, + {IDS_CMDFACTORY_ERR_INVALID_CMD_CR8FN, + "Command factory. Command creation function invalid for command '%s'. " + "Does function exist? Pointer assigned to it?"}, + {IDS_CMDFACTORY_ERR_CMD_NOT_REGISTERED, + "Command factory. Command '%s' not registered"}, + {IDS_CMDFACTORY_ERR_CMD_ALREADY_REGED, + "Command factory. Command '%s' by that name already registered"}, + {IDS_CMDMGR_ERR_CMD_FAILED_CREATE, + "Command manager. Command creation failed. %s"}, + {IDS_CMDMGR_ERR_CMD_INVOKER, "Command manager. %s "}, + {IDS_MI_INIT_ERR_LOG, "Log. Error occurred during initialisation %s"}, + {IDS_MI_INIT_ERR_RESOURCES, + "Resources. Error occurred during initialisation %s"}, + {IDS_MI_INIT_ERR_INIT, + "Driver. Error occurred during initialisation %s"}, + {IDS_MI_INIT_ERR_STREAMSTDIN, + "Stdin. Error occurred during initialisation %s"}, + {IDS_MI_INIT_ERR_STREAMSTDIN_OSHANDLER, "Stdin. The OS specific stdin " + "stream handler has not been " + "specified for this OS"}, + {IDS_MI_INIT_ERR_OS_STDIN_HANDLER, + "Stdin handler. Error occurred during initialisation %s"}, + {IDS_MI_INIT_ERR_STREAMSTDOUT, + "Stdout. Error occurred during initialisation %s"}, + {IDS_MI_INIT_ERR_STREAMSTDERR, + "Stderr. Error occurred during initialisation %s"}, + {IDS_MI_INIT_ERR_FALLTHRUDRIVER, + "Fall Through Driver. Error occurred during initialisation %s"}, + {IDS_MI_INIT_ERR_THREADMGR, + "Thread Mgr. Error occurred during initialisation %s"}, + {IDS_MI_INIT_ERR_CMDINTERPRETER, "Command interpreter. %s"}, + {IDS_MI_INIT_ERR_CMDMGR, "Command manager. %s"}, + {IDS_MI_INIT_ERR_CMDFACTORY, "Command factory. %s"}, + {IDS_MI_INIT_ERR_CMDINVOKER, "Command invoker. %s"}, + {IDS_MI_INIT_ERR_CMDMONITOR, "Command monitor. %s"}, + {IDS_MI_INIT_ERR_LLDBDEBUGGER, "LLDB Debugger. %s"}, + {IDS_MI_INIT_ERR_DRIVERMGR, "Driver manager. %s"}, + {IDS_MI_INIT_ERR_DRIVER, "Driver. %s"}, + {IDS_MI_INIT_ERR_OUTOFBANDHANDLER, "Out-of-band handler. %s "}, + {IDS_MI_INIT_ERR_DEBUGSESSIONINFO, "LLDB debug session info. %s "}, + {IDS_MI_INIT_ERR_THREADMANAGER, "Unable to init thread manager."}, + {IDS_MI_INIT_ERR_CLIENT_USING_DRIVER, + "Initialising the client to this driver failed."}, + {IDS_MI_INIT_ERR_LOCAL_DEBUG_SESSION, + "Initialising a local debug session failed."}, + {IDS_CODE_ERR_INVALID_PARAMETER_VALUE, + "Code. Invalid parameter passed to function '%s'"}, + {IDS_CODE_ERR_INVALID_PARAM_NULL_POINTER, + "Code. NULL pointer passes as a parameter to function '%s'"}, + {IDS_CODE_ERR_INVALID_ENUMERATION_VALUE, + "Code. Invalid enumeration value encountered in function '%s'"}, + { + IDS_LLDBDEBUGGER_ERR_INVALIDLISTENER, + "LLDB Debugger. LLDB Listener is not valid", + }, + { + IDS_LLDBDEBUGGER_ERR_INVALIDDEBUGGER, + "LLDB Debugger. LLDB Debugger is not valid", + }, + {IDS_LLDBDEBUGGER_ERR_CLIENTDRIVER, + "LLDB Debugger. CMIDriverBase derived driver needs to be set prior to " + "CMICmnLLDBDDebugger initialisation"}, + {IDS_LLDBDEBUGGER_ERR_STARTLISTENER, + "LLDB Debugger. Starting listening events for '%s' failed"}, + {IDS_LLDBDEBUGGER_ERR_THREADCREATIONFAIL, + "LLDB Debugger. Thread creation failed '%s'"}, + {IDS_LLDBDEBUGGER_ERR_THREAD_DELETE, + "LLDB Debugger. Thread failed to delete '%s'"}, + {IDS_LLDBDEBUGGER_ERR_INVALIDBROADCASTER, + "LLDB Debugger. Invalid SB broadcaster class name '%s' "}, + {IDS_LLDBDEBUGGER_ERR_INVALIDCLIENTNAME, + "LLDB Debugger. Invalid client name '%s' "}, + {IDS_LLDBDEBUGGER_ERR_CLIENTNOTREGISTERED, + "LLDB Debugger. Client name '%s' not registered for listening events"}, + {IDS_LLDBDEBUGGER_ERR_STOPLISTENER, "LLDB Debugger. Failure occurred " + "stopping event for client '%s' " + "SBBroadcaster '%s'"}, + {IDS_LLDBDEBUGGER_ERR_BROADCASTER_NAME, + "LLDB Debugger. Broadcaster's name '%s' is not valid"}, + {IDS_LLDBDEBUGGER_WRN_UNKNOWN_EVENT, + "LLDB Debugger. Unhandled event '%s'"}, + {IDS_LLDBOUTOFBAND_ERR_UNKNOWN_EVENT, + "LLDB Out-of-band. Handling event for '%s', an event enumeration '%d' " + "not recognised"}, + {IDS_LLDBOUTOFBAND_ERR_PROCESS_INVALID, + "LLDB Out-of-band. Invalid '%s' in '%s'"}, + {IDS_LLDBOUTOFBAND_ERR_BRKPT_NOTFOUND, "LLDB Out-of-band. %s. " + "Breakpoint information for " + "breakpoint ID %d not found"}, + {IDS_LLDBOUTOFBAND_ERR_BRKPT_INFO_GET, + "LLDB Out-of-band. %s. Failed to retrieve breakpoint information for " + "for breakpoint ID %d"}, + {IDS_LLDBOUTOFBAND_ERR_BRKPT_INFO_SET, "LLDB Out-of-band. %s. Failed " + "to set breakpoint information " + "for for breakpoint ID %d"}, + {IDS_LLDBOUTOFBAND_ERR_FORM_MI_RESPONSE, + "LLDB Out-of-band. %s. Failed to form the MI Out-of-band response"}, + {IDS_LLDBOUTOFBAND_ERR_FRAME_INFO_GET, + "LLDB Out-of-band. %s. Failed to retrieve frame information"}, + {IDS_LLDBOUTOFBAND_ERR_SETNEWDRIVERSTATE, + "LLDB Out-of-band. %s. Event handler tried to set new MI Driver " + "running state and failed. %s"}, + {IDS_LLDBOUTOFBAND_ERR_BRKPT_CNT_EXCEEDED, + "LLDB Out-of-band. '%s'. Number of valid breakpoint exceeded %d. " + "Cannot create new breakpoint with ID %d"}, + {IDS_DBGSESSION_ERR_SHARED_DATA_ADD, "LLDB debug session info. Failed " + "to add '%s' data to the shared " + "data command container"}, + {IDS_MI_SHTDWN_ERR_LOG, "Log. Error occurred during shutdown. %s"}, + {IDS_MI_SHUTDOWN_ERR, "Server shutdown failure. %s"}, + {IDE_MI_SHTDWN_ERR_RESOURCES, + "Resources. Error occurred during shutdown. %s"}, + {IDE_MI_SHTDWN_ERR_STREAMSTDIN, + "Stdin. Error occurred during shutdown. %s"}, + {IDS_MI_SHTDWN_ERR_OS_STDIN_HANDLER, + "Stdin handler. Error occurred during shutdown. %s"}, + {IDS_MI_SHTDWN_ERR_STREAMSTDOUT, + "Stdout. Error occurred during shutdown. %s"}, + {IDS_MI_SHTDWN_ERR_STREAMSTDERR, + "Stderr. Error occurred during shutdown. %s"}, + {IDS_MI_SHTDWN_ERR_THREADMGR, + "Thread Mgr. Error occurred during shutdown. %s"}, + {IDS_MI_SHTDWN_ERR_CMDINTERPRETER, + "Command interpreter. Error occurred during shutdown. %s"}, + {IDS_MI_SHTDWN_ERR_CMDMGR, + "Command manager. Error occurred during shutdown. %s"}, + {IDS_MI_SHTDWN_ERR_CMDFACTORY, + "Command factory. Error occurred during shutdown. %s"}, + {IDS_MI_SHTDWN_ERR_CMDMONITOR, + "Command invoker. Error occurred during shutdown. %s"}, + {IDS_MI_SHTDWN_ERR_CMDINVOKER, + "Command monitor. Error occurred during shutdown. %s"}, + {IDS_MI_SHTDWN_ERR_LLDBDEBUGGER, + "LLDB Debugger. Error occurred during shutdown. %s"}, + {IDS_MI_SHTDWN_ERR_DRIVERMGR, + "Driver manager. Error occurred during shutdown. %s"}, + {IDS_MI_SHTDWN_ERR_DRIVER, + "Driver. Error occurred during shutdown. %s"}, + {IDS_MI_SHTDWN_ERR_OUTOFBANDHANDLER, + "Out-of-band handler. Error occurred during shutdown. %s"}, + {IDS_MI_SHTDWN_ERR_DEBUGSESSIONINFO, + "LLDB debug session info. Error occurred during shutdown. %s"}, + {IDE_MI_SHTDWN_ERR_THREADMANAGER, "Unable to shutdown thread manager"}, + {IDS_DRIVER_ERR_PARSE_ARGS, + "Driver. Driver '%s'. Parse args error '%s'"}, + {IDS_DRIVER_ERR_PARSE_ARGS_UNKNOWN, + "Driver. Driver '%s'. Parse args error unknown"}, + {IDS_DRIVER_ERR_CURRENT_NOT_SET, + "Driver. Current working driver has not been set. Call " + "CMIDriverMgr::SetUseThisDriverToDoWork()"}, + {IDS_DRIVER_ERR_NON_REGISTERED, "Driver. No suitable drivers " + "registered with the CMIDriverMgr to " + "do work"}, + {IDS_DRIVER_SAY_DRIVER_USING, "Driver. Using driver '%s' internally"}, + {IDS_DRIVER_ERR_ID_INVALID, "Driver. Driver '%s' invalid ID '%s'"}, + {IDS_DRIVER_ERR_FALLTHRU_DRIVER_ERR, + "Driver. Fall through driver '%s' (ID:'%s') error '%s'"}, + {IDS_DRIVER_CMD_RECEIVED, + "Driver. Received command '%s'. It was %shandled%s"}, + {IDS_DRIVER_CMD_NOT_IN_FACTORY, + ". Command '%s' not in Command Factory"}, + { + IDS_DRIVER_ERR_DRIVER_STATE_ERROR, + "Driver. Driver running state error. Cannot go to next state from " + "present state as not allowed", + }, + {IDS_DRIVER_WAITING_STDIN_DATA, "Driver. Main thread suspended waiting " + "on Stdin Monitor to resume main " + "thread"}, + {IDS_DRIVER_ERR_MAINLOOP, "Driver. Error in do main loop. %s"}, + {IDS_DRIVER_ERR_LOCAL_DEBUG_NOT_IMPL, "Driver. --executable argument " + "given. Local debugging is not " + "implemented."}, + {IDS_DRIVER_ERR_LOCAL_DEBUG_INIT, "Driver. --executable argument " + "given. Initialising local debugging " + "failed."}, + {IDS_STDERR_ERR_NOT_ALL_DATA_WRITTEN, + "Stderr. Not all data was written to stream. The data '%s'"}, + {IDS_CMD_ARGS_ERR_OPTION_NOT_FOUND, + "Command Args. Option '%s' not found"}, + {IDS_CMD_ARGS_ERR_VALIDATION_MANDATORY, "Mandatory args not found: %s"}, + {IDS_CMD_ARGS_ERR_VALIDATION_INVALID, "Invalid args: %s"}, + {IDS_CMD_ARGS_ERR_VALIDATION_MAN_INVALID, + "Mandatory args not found: %s. Invalid args: %s"}, + {IDS_CMD_ARGS_ERR_VALIDATION_MISSING_INF, + "Args missing additional information: %s"}, + {IDS_CMD_ARGS_ERR_CONTEXT_NOT_ALL_EATTEN, + "Not all arguments or options were recognised: %s"}, + {IDS_CMD_ARGS_ERR_PREFIX_MSG, "Command Args. Validation failed. "}, + {IDS_VARIANT_ERR_USED_BASECLASS, "Variant container: Variant object " + "used the base class. See " + "CMIUtilVariant"}, + {IDS_VARIANT_ERR_MAP_KEY_INVALID, "Variant container: Invalid ID '%s'"}, + {IDS_WORD_INVALIDBRKTS, ""}, + {IDS_WORD_NONE, "None"}, + {IDS_WORD_NOT, "not"}, + {IDS_WORD_INVALIDEMPTY, ""}, + {IDS_WORD_INVALIDNULLPTR, ""}, + {IDS_WORD_UNKNOWNBRKTS, ""}, + {IDS_WORD_NOT_IMPLEMENTED, "Not implemented"}, + {IDS_WORD_NOT_IMPLEMENTED_BRKTS, ""}, + {IDS_WORD_UNKNOWNTYPE_BRKTS, ""}, + {IDS_WORD_ERR_MSG_NOT_IMPLEMENTED_BRKTS, + ""}, + {IDS_CMD_ERR_N_OPTIONS_REQUIRED, + "Command '%s'. Missing options, %d required"}, + {IDS_CMD_ERR_OPTION_NOT_FOUND, "Command '%s'. Option '%s' not found"}, + {IDS_CMD_ERR_ARGS, "Command '%s'. %s"}, + {IDS_CMD_WRN_ARGS_NOT_HANDLED, "Command '%s'. Warning the following " + "options not handled by the command: " + "%s"}, + {IDS_CMD_ERR_FNFAILED, "Command '%s'. Fn '%s' failed"}, + {IDS_CMD_ERR_SHARED_DATA_NOT_FOUND, + "Command '%s'. Shared data '%s' not found"}, + {IDS_CMD_ERR_LLDBPROCESS_DETACH, + "Command '%s'. Process detach failed. '%s'"}, + {IDS_CMD_ERR_LLDBPROCESS_DESTROY, + "Command '%s'. Process destroy failed. '%s'"}, + {IDS_CMD_ERR_SETWKDIR, + "Command '%s'. Failed to set working directory '%s'"}, + {IDS_CMD_ERR_INVALID_TARGET, + "Command '%s'. Target binary '%s' is invalid. %s"}, + {IDS_CMD_ERR_INVALID_TARGET_CURRENT, + "Command '%s'. Current SBTarget is invalid"}, + {IDS_CMD_ERR_INVALID_TARGET_TYPE, + "Command '%s'. Target type '%s' is not recognised"}, + {IDS_CMD_ERR_INVALID_TARGET_PLUGIN, + "Command '%s'. Target plugin is invalid. %s"}, + {IDS_CMD_ERR_CONNECT_TO_TARGET, + "Command '%s'. Error connecting to target: '%s'"}, + {IDS_CMD_ERR_INVALID_TARGETPLUGINCURRENT, + "Command '%s'. Current target plugin is invalid"}, + {IDS_CMD_ERR_NOT_IMPLEMENTED, "Command '%s'. Command not implemented"}, + {IDS_CMD_ERR_NOT_IMPLEMENTED_DEPRECATED, + "Command '%s'. Command not implemented as it has been deprecated"}, + {IDS_CMD_ERR_CREATE_TARGET, "Command '%s'. Create target failed: %s"}, + {IDS_CMD_ERR_BRKPT_LOCATION_FORMAT, + "Command '%s'. Incorrect format for breakpoint location '%s'"}, + {IDS_CMD_ERR_BRKPT_LOCATION_NOT_FOUND, + "Command '%s'. Breakpoint location '%s' not found"}, + {IDS_CMD_ERR_BRKPT_INVALID, "Command '%s'. Breakpoint '%s' invalid"}, + {IDS_CMD_ERR_BRKPT_CNT_EXCEEDED, "Command '%s'. Number of valid " + "breakpoint exceeded %d. Cannot " + "create new breakpoint '%s'"}, + {IDS_CMD_ERR_SOME_ERROR, "Command '%s'. Error: %s"}, + {IDS_CMD_ERR_THREAD_INVALID, "Command '%s'. Thread ID invalid"}, + {IDS_CMD_ERR_THREAD_FRAME_RANGE_INVALID, + "Command '%s'. Thread frame range invalid"}, + {IDS_CMD_ERR_FRAME_INVALID, "Command '%s'. Frame ID invalid"}, + {IDS_CMD_ERR_VARIABLE_DOESNOTEXIST, + "Command '%s'. Variable '%s' does not exist"}, + {IDS_CMD_ERR_VARIABLE_ENUM_INVALID, "Command '%s'. Invalid enumeration " + "for variable '%s' formatted " + "string '%s'"}, + {IDS_CMD_ERR_VARIABLE_EXPRESSIONPATH, + "Command '%s'. Failed to get expression for variable '%s'"}, + {IDS_CMD_ERR_VARIABLE_CREATION_FAILED, + "Failed to create variable object for '%s'"}, + {IDS_CMD_ERR_VARIABLE_CHILD_RANGE_INVALID, + "Command '%s'. Variable children range invalid"}, + {IDS_CMD_ERR_CMD_RUN_BUT_NO_ACTION, ""}, + {IDS_CMD_ERR_EVENT_HANDLED_BUT_NO_ACTION, + ""}, + {IDS_CMD_ERR_DISASM_ADDR_START_INVALID, + "Command '%s'. Invalid start value '%s'"}, + {IDS_CMD_ERR_DISASM_ADDR_END_INVALID, + "Command '%s'. Invalid end value '%s'"}, + {IDS_CMD_ERR_MEMORY_ALLOC_FAILURE, + "Command '%s'. Failed to allocate memory %d bytes"}, + {IDS_CMD_ERR_LLDB_ERR_NOT_READ_WHOLE_BLK, + "Command '%s'. LLDB unable to read entire memory block of %u bytes at " + "address 0x%016" PRIx64}, + {IDS_CMD_ERR_LLDB_ERR_READ_MEM_BYTES, "Command '%s'. Unable to read " + "memory block of %u bytes at " + "address 0x%016" PRIx64 ": %s "}, + {IDS_CMD_ERR_INVALID_PROCESS, + "Command '%s'. Invalid process during debug session"}, + {IDS_CMD_ERR_INVALID_PRINT_VALUES, + "Command '%s'. Unknown value for PRINT_VALUES: must be: 0 or " + "\"--no-values\", 1 or \"--all-values\", 2 or \"--simple-values\""}, + {IDS_CMD_ERR_INVALID_LOCATION_FORMAT, + "Command '%s'. Invalid location format '%s'"}, + {IDS_CMD_ERR_INVALID_FORMAT_TYPE, + "Command '%s'. Invalid var format type '%s'"}, + {IDS_CMD_ERR_BRKPT_INFO_OBJ_NOT_FOUND, + "Command '%s'. Breakpoint information for breakpoint ID %d not found"}, + {IDS_CMD_ERR_LLDB_ERR_WRITE_MEM_BYTES, "Command '%s'. Unable to write " + "memory block of %u bytes at " + "address 0x%016" PRIx64 ": %s "}, + {IDS_CMD_ERR_LLDB_ERR_NOT_WRITE_WHOLEBLK, + "Command '%s'. LLDB unable to write entire memory block of %u bytes " + "at address 0x%016" PRIX64}, + {IDS_CMD_ERR_SET_NEW_DRIVER_STATE, "Command '%s'. Command tried to set " + "new MI Driver running state and " + "failed. %s"}, + {IDS_CMD_ERR_INFO_PRINTFN_NOT_FOUND, + "The request '%s' was not recognised, not implemented"}, + {IDS_CMD_ERR_INFO_PRINTFN_FAILED, "The request '%s' failed."}, + {IDS_CMD_ERR_GDBSET_OPT_TARGETASYNC, + "'target-async' expects \"on\" or \"off\""}, + {IDS_CMD_ERR_GDBSET_OPT_BREAKPOINT, + "'breakpoint' expects \"pending on\" or \"pending off\""}, + {IDS_CMD_ERR_GDBSET_OPT_SOLIBSEARCHPATH, + "'solib-search-path' requires at least one argument"}, + {IDS_CMD_ERR_GDBSET_OPT_PRINT_BAD_ARGS, + "'print' expects option-name and \"on\" or \"off\""}, + {IDS_CMD_ERR_GDBSET_OPT_PRINT_UNKNOWN_OPTION, + "'print' error. The option '%s' not found"}, + {IDS_CMD_ERR_GDBSHOW_OPT_PRINT_BAD_ARGS, + "'print' expects option-name and \"on\" or \"off\""}, + {IDS_CMD_ERR_GDBSHOW_OPT_PRINT_UNKNOWN_OPTION, + "'print' error. The option '%s' not found"}, + {IDS_CMD_ERR_GDBSHOW_OPT_BREAKPOINT_BAD_ARGS, + "'breakpoint' expects option-name"}, + {IDS_CMD_ERR_GDBSHOW_OPT_BREAKPOINT_UNKNOWN_OPTION, + "'breakpoint' error. The option '%s' not found"}, + {IDS_CMD_ERR_EXPR_INVALID, "Failed to evaluate expression: %s"}, + {IDS_CMD_ERR_ATTACH_FAILED, + "Command '%s'. Attach to process failed: %s"}, + {IDS_CMD_ERR_ATTACH_BAD_ARGS, + "Command '%s'. Must specify either a PID or a Name"}}; + +//++ +// Details: CMICmnResources constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnResources::CMICmnResources() : m_nResourceId2TextDataSize(0) { + // Do not use this constructor, use Initialize() +} + +//++ +// Details: CMICmnResources destructor. +// Type: Overridden. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnResources::~CMICmnResources() { + // Do not use this destructor, use Shutdown() +} + +//++ +// Details: Initialize the resources and set locality for the server. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnResources::Initialize() { + m_clientUsageRefCnt++; + + if (m_bInitialized) + return MIstatus::success; + + m_bInitialized = ReadResourceStringData(); + + return m_bInitialized; +} + +//++ +// Details: Release resources for *this object. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnResources::Shutdown() { + if (--m_clientUsageRefCnt > 0) + return MIstatus::success; + + if (!m_bInitialized) + return MIstatus::success; + + // Tear down resource explicitly + m_mapRscrIdToTextData.clear(); + + m_bInitialized = false; + + return MIstatus::success; +} + +//++ +// Details: Initialize the resources and set locality for the server. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnResources::ReadResourceStringData() { + m_nResourceId2TextDataSize = + sizeof ms_pResourceId2TextData / sizeof ms_pResourceId2TextData[0]; + for (MIuint i = 0; i < m_nResourceId2TextDataSize; i++) { + const SRsrcTextData *pRscrData = &ms_pResourceId2TextData[i]; + MapPairRscrIdToTextData_t pr(pRscrData->id, pRscrData->pTextData); + m_mapRscrIdToTextData.insert(pr); + } + + return MIstatus::success; +} + +//++ +// Details: Retrieve the corresponding text assigned to the resource ID. +// Type: Method. +// Args: vResourceId - (R) MI resource ID. +// Return: CMIUtilString - Resource text. +// Throws: None. +//-- +CMIUtilString CMICmnResources::GetString(const MIuint vResourceId) const { + CMIUtilString str; + const bool bFound = GetStringFromResource(vResourceId, str); + MIunused(bFound); + assert(bFound); + + return str; +} + +//++ +// Details: Determine the MI resource ID existings. +// Type: Method. +// Args: vResourceId - (R) MI resource ID. +// Return: True - Exists. +// False - Not found. +// Throws: None. +//-- +bool CMICmnResources::HasString(const MIuint vResourceId) const { + CMIUtilString str; + return GetStringFromResource(vResourceId, str); +} + +//++ +// Details: Retrieve the resource text data for the given resource ID. If a +// resource ID +// cannot be found and error is given returning the ID of the resource +// that +// cannot be located. +// Type: Method. +// Args: vResourceId - (R) MI resource ID. +// vrwResourceString - (W) Text. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnResources::GetStringFromResource( + const MIuint vResourceId, CMIUtilString &vrwResourceString) const { + MapRscrIdToTextData_t::const_iterator it = + m_mapRscrIdToTextData.find(vResourceId); + if (it == m_mapRscrIdToTextData.end()) { + // Check this is a static variable init that needs this before we are ready + if (!m_bInitialized) { + (const_cast(this))->Initialize(); + it = m_mapRscrIdToTextData.find(vResourceId); + if (it == m_mapRscrIdToTextData.end()) { + vrwResourceString = MIRSRC(IDS_RESOURCES_ERR_STRING_TABLE_INVALID); + return MIstatus::failure; + } + } + + if (it == m_mapRscrIdToTextData.end()) { + vrwResourceString = CMIUtilString::Format( + MIRSRC(IDS_RESOURCES_ERR_STRING_NOT_FOUND), vResourceId); + return MIstatus::failure; + } + } + + const MIuint nRsrcId((*it).first); + MIunused(nRsrcId); + const char *pRsrcData((*it).second); + + // Return result + vrwResourceString = pRsrcData; + + return MIstatus::success; +} diff --git a/src/MICmnResources.h b/src/MICmnResources.h new file mode 100644 index 00000000000..111b0c33899 --- /dev/null +++ b/src/MICmnResources.h @@ -0,0 +1,339 @@ +//===-- MICmnResources.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// Third party headers +#include + +// In-house headers: +#include "MICmnBase.h" +#include "MIUtilSingletonBase.h" +#include "MIUtilString.h" + +//++ +//============================================================================ +// Details: MI string test data resource definitions. These IDs match up with +// actual string data in a map internal to CMICmnResources. +// *** Be sure to update ms_pResourceId2TextData[] array **** +enum { + IDS_PROJNAME = 1, + IDS_MI_VERSION_DESCRIPTION_DEBUG, + IDS_MI_VERSION_DESCRIPTION, + IDS_MI_APPNAME_SHORT, + IDS_MI_APPNAME_LONG, + IDS_MI_APP_FILEPATHNAME, + IDS_MI_APP_ARGS, + IDE_MI_VERSION_GDB, + + IDS_UTIL_FILE_ERR_INVALID_PATHNAME, + IDS_UTIL_FILE_ERR_OPENING_FILE, + IDS_UTIL_FILE_ERR_OPENING_FILE_UNKNOWN, + IDE_UTIL_FILE_ERR_WRITING_FILE, + IDE_UTIL_FILE_ERR_WRITING_NOTOPEN, + + IDS_RESOURCES_ERR_STRING_NOT_FOUND, + IDS_RESOURCES_ERR_STRING_TABLE_INVALID, + + IDS_MI_CLIENT_MSG, + + IDS_LOG_MSG_CREATION_DATE, + IDS_LOG_MSG_FILE_LOGGER_PATH, + IDS_LOG_MSG_VERSION, + IDS_LOG_ERR_FILE_LOGGER_DISABLED, + IDS_LOG_MEDIUM_ERR_INIT, + IDS_LOG_MEDIUM_ERR_WRITE_ANY, + IDS_LOG_MEDIUM_ERR_WRITE_MEDIUMFAIL, + + IDS_MEDIUMFILE_NAME, + IDS_MEDIUMFILE_ERR_INVALID_PATH, + IDS_MEDIUMFILE_ERR_FILE_HEADER, + IDS_MEDIUMFILE_NAME_LOG, + + IDE_OS_ERR_UNKNOWN, + IDE_OS_ERR_RETRIEVING, + + IDS_DRIVERMGR_DRIVER_ERR_INIT, + + IDE_MEDIUMSTDERR_NAME, + IDE_MEDIUMSTDOUT_NAME, + + IDE_MI_APP_DESCRIPTION, + IDE_MI_APP_INFORMATION, + IDE_MI_APP_ARG_USAGE, + IDE_MI_APP_ARG_HELP, + IDE_MI_APP_ARG_VERSION, + IDE_MI_APP_ARG_VERSION_LONG, + IDE_MI_APP_ARG_INTERPRETER, + IDE_MI_APP_ARG_EXECUTEABLE, + IDE_MI_APP_ARG_SOURCE, + IDE_MI_APP_ARG_APP_LOG, + IDE_MI_APP_ARG_APP_LOG_DIR, + IDE_MI_APP_ARG_EXAMPLE, + IDE_MI_APP_ARG_EXECUTABLE, + IDE_MI_APP_ARG_SYNCHRONOUS, + + IDS_STDIN_ERR_INVALID_PROMPT, + IDS_STDIN_ERR_THREAD_CREATION_FAILED, + IDS_STDIN_ERR_THREAD_DELETE, + IDS_STDIN_ERR_CHKING_BYTE_AVAILABLE, + IDS_STDIN_INPUT_CTRL_CHARS, + + IDS_CMD_QUIT_HELP, + + IDS_THREADMGR_ERR_THREAD_ID_INVALID, + IDS_THREADMGR_ERR_THREAD_FAIL_CREATE, + IDS_THREADMGR_ERR_THREAD_ID_NOT_FOUND, + IDS_THREADMGR_ERR_THREAD_STILL_ALIVE, + + IDS_FALLTHRU_DRIVER_CMD_RECEIVED, + + IDS_CMDFACTORY_ERR_INVALID_CMD_NAME, + IDS_CMDFACTORY_ERR_INVALID_CMD_CR8FN, + IDS_CMDFACTORY_ERR_CMD_NOT_REGISTERED, + IDS_CMDFACTORY_ERR_CMD_ALREADY_REGED, + + IDS_CMDMGR_ERR_CMD_FAILED_CREATE, + IDS_CMDMGR_ERR_CMD_INVOKER, + + IDS_MI_INIT_ERR_LOG, + IDS_MI_INIT_ERR_RESOURCES, + IDS_MI_INIT_ERR_INIT, + IDS_MI_INIT_ERR_STREAMSTDIN, + IDS_MI_INIT_ERR_STREAMSTDIN_OSHANDLER, + IDS_MI_INIT_ERR_OS_STDIN_HANDLER, + IDS_MI_INIT_ERR_STREAMSTDOUT, + IDS_MI_INIT_ERR_STREAMSTDERR, + IDS_MI_INIT_ERR_FALLTHRUDRIVER, + IDS_MI_INIT_ERR_THREADMGR, + IDS_MI_INIT_ERR_CMDINTERPRETER, + IDS_MI_INIT_ERR_CMDMGR, + IDS_MI_INIT_ERR_CMDFACTORY, + IDS_MI_INIT_ERR_CMDINVOKER, + IDS_MI_INIT_ERR_CMDMONITOR, + IDS_MI_INIT_ERR_LLDBDEBUGGER, + IDS_MI_INIT_ERR_DRIVERMGR, + IDS_MI_INIT_ERR_DRIVER, + IDS_MI_INIT_ERR_OUTOFBANDHANDLER, + IDS_MI_INIT_ERR_DEBUGSESSIONINFO, + IDS_MI_INIT_ERR_THREADMANAGER, + IDS_MI_INIT_ERR_CLIENT_USING_DRIVER, + IDS_MI_INIT_ERR_LOCAL_DEBUG_SESSION, + + IDS_CODE_ERR_INVALID_PARAMETER_VALUE, + IDS_CODE_ERR_INVALID_PARAM_NULL_POINTER, + IDS_CODE_ERR_INVALID_ENUMERATION_VALUE, + + IDS_LLDBDEBUGGER_ERR_INVALIDLISTENER, + IDS_LLDBDEBUGGER_ERR_INVALIDDEBUGGER, + IDS_LLDBDEBUGGER_ERR_CLIENTDRIVER, + IDS_LLDBDEBUGGER_ERR_STARTLISTENER, + IDS_LLDBDEBUGGER_ERR_THREADCREATIONFAIL, + IDS_LLDBDEBUGGER_ERR_THREAD_DELETE, + IDS_LLDBDEBUGGER_ERR_INVALIDBROADCASTER, + IDS_LLDBDEBUGGER_ERR_INVALIDCLIENTNAME, + IDS_LLDBDEBUGGER_ERR_CLIENTNOTREGISTERED, + IDS_LLDBDEBUGGER_ERR_STOPLISTENER, + IDS_LLDBDEBUGGER_ERR_BROADCASTER_NAME, + IDS_LLDBDEBUGGER_WRN_UNKNOWN_EVENT, + + IDS_LLDBOUTOFBAND_ERR_UNKNOWN_EVENT, + IDS_LLDBOUTOFBAND_ERR_PROCESS_INVALID, + IDS_LLDBOUTOFBAND_ERR_BRKPT_NOTFOUND, + IDS_LLDBOUTOFBAND_ERR_BRKPT_INFO_GET, + IDS_LLDBOUTOFBAND_ERR_BRKPT_INFO_SET, + IDS_LLDBOUTOFBAND_ERR_FORM_MI_RESPONSE, + IDS_LLDBOUTOFBAND_ERR_FRAME_INFO_GET, + IDS_LLDBOUTOFBAND_ERR_SETNEWDRIVERSTATE, + IDS_LLDBOUTOFBAND_ERR_BRKPT_CNT_EXCEEDED, + + IDS_DBGSESSION_ERR_SHARED_DATA_ADD, + + IDS_MI_SHTDWN_ERR_LOG, + IDS_MI_SHUTDOWN_ERR, + IDE_MI_SHTDWN_ERR_RESOURCES, + IDE_MI_SHTDWN_ERR_STREAMSTDIN, + IDS_MI_SHTDWN_ERR_OS_STDIN_HANDLER, + IDS_MI_SHTDWN_ERR_STREAMSTDOUT, + IDS_MI_SHTDWN_ERR_STREAMSTDERR, + IDS_MI_SHTDWN_ERR_THREADMGR, + IDS_MI_SHTDWN_ERR_CMDINTERPRETER, + IDS_MI_SHTDWN_ERR_CMDMGR, + IDS_MI_SHTDWN_ERR_CMDFACTORY, + IDS_MI_SHTDWN_ERR_CMDINVOKER, + IDS_MI_SHTDWN_ERR_CMDMONITOR, + IDS_MI_SHTDWN_ERR_LLDBDEBUGGER, + IDS_MI_SHTDWN_ERR_DRIVERMGR, + IDS_MI_SHTDWN_ERR_DRIVER, + IDS_MI_SHTDWN_ERR_OUTOFBANDHANDLER, + IDS_MI_SHTDWN_ERR_DEBUGSESSIONINFO, + IDE_MI_SHTDWN_ERR_THREADMANAGER, + + IDS_DRIVER_ERR_PARSE_ARGS, + IDS_DRIVER_ERR_PARSE_ARGS_UNKNOWN, + IDS_DRIVER_ERR_CURRENT_NOT_SET, + IDS_DRIVER_ERR_NON_REGISTERED, + IDS_DRIVER_SAY_DRIVER_USING, + IDS_DRIVER_ERR_ID_INVALID, + IDS_DRIVER_ERR_FALLTHRU_DRIVER_ERR, + IDS_DRIVER_CMD_RECEIVED, + IDS_DRIVER_CMD_NOT_IN_FACTORY, + IDS_DRIVER_ERR_DRIVER_STATE_ERROR, + IDS_DRIVER_ERR_MAINLOOP, + IDS_DRIVER_ERR_LOCAL_DEBUG_NOT_IMPL, + IDS_DRIVER_ERR_LOCAL_DEBUG_INIT, + + IDS_DRIVER_WAITING_STDIN_DATA, + + IDS_STDERR_ERR_NOT_ALL_DATA_WRITTEN, + + IDS_CMD_ARGS_ERR_OPTION_NOT_FOUND, + IDS_CMD_ARGS_ERR_VALIDATION_MANDATORY, + IDS_CMD_ARGS_ERR_VALIDATION_INVALID, + IDS_CMD_ARGS_ERR_VALIDATION_MAN_INVALID, + IDS_CMD_ARGS_ERR_VALIDATION_MISSING_INF, + IDS_CMD_ARGS_ERR_CONTEXT_NOT_ALL_EATTEN, + IDS_CMD_ARGS_ERR_PREFIX_MSG, + + IDS_VARIANT_ERR_USED_BASECLASS, + IDS_VARIANT_ERR_MAP_KEY_INVALID, + + IDS_WORD_INVALIDBRKTS, + IDS_WORD_NONE, + IDS_WORD_NOT, + IDS_WORD_INVALIDEMPTY, + IDS_WORD_INVALIDNULLPTR, + IDS_WORD_UNKNOWNBRKTS, + IDS_WORD_NOT_IMPLEMENTED, + IDS_WORD_NOT_IMPLEMENTED_BRKTS, + IDS_WORD_UNKNOWNTYPE_BRKTS, + IDS_WORD_ERR_MSG_NOT_IMPLEMENTED_BRKTS, + + IDS_CMD_ERR_N_OPTIONS_REQUIRED, + IDS_CMD_ERR_OPTION_NOT_FOUND, + IDS_CMD_ERR_ARGS, + IDS_CMD_WRN_ARGS_NOT_HANDLED, + IDS_CMD_ERR_FNFAILED, + IDS_CMD_ERR_SHARED_DATA_NOT_FOUND, + IDS_CMD_ERR_LLDBPROCESS_DETACH, + IDS_CMD_ERR_LLDBPROCESS_DESTROY, + IDS_CMD_ERR_SETWKDIR, + IDS_CMD_ERR_INVALID_TARGET, + IDS_CMD_ERR_INVALID_TARGET_CURRENT, + IDS_CMD_ERR_INVALID_TARGET_TYPE, + IDS_CMD_ERR_INVALID_TARGET_PLUGIN, + IDS_CMD_ERR_CONNECT_TO_TARGET, + IDS_CMD_ERR_INVALID_TARGETPLUGINCURRENT, + IDS_CMD_ERR_NOT_IMPLEMENTED, + IDS_CMD_ERR_NOT_IMPLEMENTED_DEPRECATED, + IDS_CMD_ERR_CREATE_TARGET, + IDS_CMD_ERR_BRKPT_LOCATION_FORMAT, + IDS_CMD_ERR_BRKPT_LOCATION_NOT_FOUND, + IDS_CMD_ERR_BRKPT_INVALID, + IDS_CMD_ERR_BRKPT_CNT_EXCEEDED, + IDS_CMD_ERR_SOME_ERROR, + IDS_CMD_ERR_THREAD_INVALID, + IDS_CMD_ERR_THREAD_FRAME_RANGE_INVALID, + IDS_CMD_ERR_FRAME_INVALID, + IDS_CMD_ERR_VARIABLE_DOESNOTEXIST, + IDS_CMD_ERR_VARIABLE_ENUM_INVALID, + IDS_CMD_ERR_VARIABLE_EXPRESSIONPATH, + IDS_CMD_ERR_VARIABLE_CREATION_FAILED, + IDS_CMD_ERR_VARIABLE_CHILD_RANGE_INVALID, + IDS_CMD_ERR_CMD_RUN_BUT_NO_ACTION, + IDS_CMD_ERR_EVENT_HANDLED_BUT_NO_ACTION, + IDS_CMD_ERR_DISASM_ADDR_START_INVALID, + IDS_CMD_ERR_DISASM_ADDR_END_INVALID, + IDS_CMD_ERR_MEMORY_ALLOC_FAILURE, + IDS_CMD_ERR_LLDB_ERR_NOT_READ_WHOLE_BLK, + IDS_CMD_ERR_LLDB_ERR_READ_MEM_BYTES, + IDS_CMD_ERR_INVALID_PROCESS, + IDS_CMD_ERR_INVALID_PRINT_VALUES, + IDS_CMD_ERR_INVALID_LOCATION_FORMAT, + IDS_CMD_ERR_INVALID_FORMAT_TYPE, + IDS_CMD_ERR_BRKPT_INFO_OBJ_NOT_FOUND, + IDS_CMD_ERR_LLDB_ERR_WRITE_MEM_BYTES, + IDS_CMD_ERR_LLDB_ERR_NOT_WRITE_WHOLEBLK, + IDS_CMD_ERR_SET_NEW_DRIVER_STATE, + IDS_CMD_ERR_INFO_PRINTFN_NOT_FOUND, + IDS_CMD_ERR_INFO_PRINTFN_FAILED, + IDS_CMD_ERR_GDBSET_OPT_TARGETASYNC, + IDS_CMD_ERR_GDBSET_OPT_BREAKPOINT, + IDS_CMD_ERR_GDBSET_OPT_SOLIBSEARCHPATH, + IDS_CMD_ERR_GDBSET_OPT_PRINT_BAD_ARGS, + IDS_CMD_ERR_GDBSET_OPT_PRINT_UNKNOWN_OPTION, + IDS_CMD_ERR_GDBSHOW_OPT_PRINT_BAD_ARGS, + IDS_CMD_ERR_GDBSHOW_OPT_PRINT_UNKNOWN_OPTION, + IDS_CMD_ERR_GDBSHOW_OPT_BREAKPOINT_BAD_ARGS, + IDS_CMD_ERR_GDBSHOW_OPT_BREAKPOINT_UNKNOWN_OPTION, + IDS_CMD_ERR_EXPR_INVALID, + IDS_CMD_ERR_ATTACH_FAILED, + IDS_CMD_ERR_ATTACH_BAD_ARGS +}; + +//++ +//============================================================================ +// Details: MI common code implementation class. Handle application resources +// and locality. +// Singleton class. +//-- +class CMICmnResources : public CMICmnBase, + public MI::ISingleton { + friend class MI::ISingleton; + + // Methods: +public: + bool Initialize() override; + bool Shutdown() override; + + CMIUtilString GetString(const MIuint vResourceId) const; + bool HasString(const MIuint vResourceId) const; + + // Typedef: +private: + typedef std::map MapRscrIdToTextData_t; + typedef std::pair MapPairRscrIdToTextData_t; + + // Enumerations: +private: + enum Buffer_e { eBufferSize = 2048 }; + + // Structs: +private: + struct SRsrcTextData { + MIuint id; + const char *pTextData; + }; + + // Methods: +private: + /* ctor */ CMICmnResources(); + /* ctor */ CMICmnResources(const CMICmnResources &); + void operator=(const CMICmnResources &); + + bool GetStringFromResource(const MIuint vResourceId, + CMIUtilString &vrwResourceString) const; + bool ReadResourceStringData(); + + // Overridden: +private: + // From CMICmnBase + /* dtor */ ~CMICmnResources() override; + + // Attributes: +private: + static const SRsrcTextData ms_pResourceId2TextData[]; + // + MIuint m_nResourceId2TextDataSize; + MapRscrIdToTextData_t m_mapRscrIdToTextData; +}; + +//++ ========================================================================= +// Details: Macro short cut for retrieving a text data resource +//-- +#define MIRSRC(x) CMICmnResources::Instance().GetString(x).c_str() diff --git a/src/MICmnStreamStderr.cpp b/src/MICmnStreamStderr.cpp new file mode 100644 index 00000000000..f4f6a3ab8fe --- /dev/null +++ b/src/MICmnStreamStderr.cpp @@ -0,0 +1,249 @@ +//===-- MICmnStreamStderr.cpp ------------------------------------*- C++ +//-*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmnStreamStderr.h" +#include "MICmnLog.h" +#include "MICmnResources.h" +#include "MIDriver.h" + +//++ +// Details: CMICmnStreamStderr constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnStreamStderr::CMICmnStreamStderr() {} + +//++ +// Details: CMICmnStreamStderr destructor. +// Type: Overridable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnStreamStderr::~CMICmnStreamStderr() { Shutdown(); } + +//++ +// Details: Initialize resources for *this stderr stream. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnStreamStderr::Initialize() { + m_clientUsageRefCnt++; + + if (m_bInitialized) + return MIstatus::success; + + bool bOk = MIstatus::success; + +#ifdef _MSC_VER +// Debugging / I/O issues with client. +// This is only required on Windows if you do not use ::flush(stderr). MI uses +// ::flush(stderr) +// It trys to ensure the process attached to the stderr steam gets ALL the data. +//::setbuf( stderr, NULL ); +#endif // _MSC_VER + + m_bInitialized = bOk; + + return MIstatus::success; +} + +//++ +// Details: Release resources for *this stderr stream. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnStreamStderr::Shutdown() { + if (--m_clientUsageRefCnt > 0) + return MIstatus::success; + + if (!m_bInitialized) + return MIstatus::success; + + ClrErrorDescription(); + + m_bInitialized = false; + + return MIstatus::success; +} + +//++ +// Details: Write text data to stderr. Prefix the message with "MI:". The text +// data does +// not need to include a carriage line return as this is added to the +// text. The +// function also then passes the text data into the CMICmnLog logger. +// Type: Method. +// Args: vText - (R) Text data. +// vbSendToLog - (R) True = Yes send to the Log file too, false = do +// not. (Dflt = true) +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnStreamStderr::Write(const CMIUtilString &vText, + const bool vbSendToLog /* = true */) { + if (vText.length() == 0) + return MIstatus::failure; + + const CMIUtilString strPrefixed(CMIUtilString::Format( + "%s: %s", CMIDriver::Instance().GetAppNameShort().c_str(), + vText.c_str())); + + return WritePriv(strPrefixed, vText, vbSendToLog); +} + +//++ +// Details: Write an LLDB text message to stderr. +// The text data does not need to include a carriage line return as +// this is added +// to the text. The function also then passes the text data into the +// CMICmnLog +// logger. +// Type: Method. +// Args: vText - (R) Text data. +// vbSendToLog - (R) True = Yes send to the Log file too, false = do +// not. (Dflt = true) +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnStreamStderr::WriteLLDBMsg(const CMIUtilString &vText, + const bool vbSendToLog /* = true */) { + if (vText.length() == 0) + return MIstatus::failure; + + const CMIUtilString strPrefixed( + CMIUtilString::Format("LLDB: %s", vText.c_str())); + + return WritePriv(vText, strPrefixed, vbSendToLog); +} + +//++ +// Details: Write text data to stderr. The text data does not need to +// include a carriage line return as this is added to the text. The +// function also +// then passes the text data into the CMICmnLog logger. +// Type: Method. +// Args: vText - (R) Text data. May be prefixed with MI app's short +// name. +// vTxtForLogFile - (R) Text data. +// vbSendToLog - (R) True = Yes send to the Log file too, false = +// do not. (Dflt = true) +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnStreamStderr::WritePriv(const CMIUtilString &vText, + const CMIUtilString &vTxtForLogFile, + const bool vbSendToLog /* = true */) { + if (vText.length() == 0) + return MIstatus::failure; + + bool bOk = MIstatus::success; + { + // Grab the stderr thread lock while we print + CMIUtilThreadLock _lock(m_mutex); + + // Send this text to stderr + const MIint status = ::fputs(vText.c_str(), stderr); + if (status == EOF) { + const CMIUtilString errMsg(CMIUtilString::Format( + MIRSRC(IDS_STDERR_ERR_NOT_ALL_DATA_WRITTEN), vText.c_str())); + SetErrorDescription(errMsg); + bOk = MIstatus::failure; + } else { + ::fprintf(stderr, "\n"); + ::fflush(stderr); + } + + // Send this text to the log + if (bOk && vbSendToLog) + bOk &= m_pLog->WriteLog(vTxtForLogFile); + } + + return bOk; +} + +//++ +// Details: Lock the availability of the stream stderr. Other users of *this +// stream will +// be stalled until it is available (Unlock()). +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnStreamStderr::Lock() { + m_mutex.Lock(); + return MIstatus::success; +} + +//++ +// Details: Release a previously locked stderr. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnStreamStderr::Unlock() { + m_mutex.Unlock(); + return MIstatus::success; +} + +//++ +// Details: Take MI Driver text message and send to the stderr stream. Also +// output to the +// MI Log file. +// Type: Static method. +// Args: vrTxt - (R) Text. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnStreamStderr::TextToStderr(const CMIUtilString &vrTxt) { + const bool bLock = CMICmnStreamStderr::Instance().Lock(); + const bool bOk = bLock && CMICmnStreamStderr::Instance().Write(vrTxt); + bLock &&CMICmnStreamStderr::Instance().Unlock(); + + return bOk; +} + +//++ +// Details: Take an LLDB message and send to the stderr stream. The message is +// not always +// an error message. The user has typed a command in to the Eclipse +// console (by- +// passing Eclipse) and this is the result message from LLDB back to +// the user. +// Also output to the MI Log file. +// Type: Static method. +// Args: vrTxt - (R) Text. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnStreamStderr::LLDBMsgToConsole(const CMIUtilString &vrTxt) { + const bool bLock = CMICmnStreamStderr::Instance().Lock(); + const bool bOk = bLock && CMICmnStreamStderr::Instance().WriteLLDBMsg(vrTxt); + bLock &&CMICmnStreamStderr::Instance().Unlock(); + + return bOk; +} diff --git a/src/MICmnStreamStderr.h b/src/MICmnStreamStderr.h new file mode 100644 index 00000000000..65eea440dd7 --- /dev/null +++ b/src/MICmnStreamStderr.h @@ -0,0 +1,61 @@ +//===-- MICmnStreamStderr.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmnBase.h" +#include "MIUtilSingletonBase.h" +#include "MIUtilString.h" +#include "MIUtilThreadBaseStd.h" + +//++ +//============================================================================ +// Details: MI common code class. The MI driver requires this object. +// CMICmnStreamStderr sets up and tears downs stderr for the driver. +// +// Singleton class. +//-- +class CMICmnStreamStderr : public CMICmnBase, + public MI::ISingleton { + friend class MI::ISingleton; + + // Statics: +public: + static bool TextToStderr(const CMIUtilString &vrTxt); + static bool LLDBMsgToConsole(const CMIUtilString &vrTxt); + + // Methods: +public: + bool Initialize() override; + bool Shutdown() override; + // + bool Lock(); + bool Unlock(); + bool Write(const CMIUtilString &vText, const bool vbSendToLog = true); + bool WriteLLDBMsg(const CMIUtilString &vText, const bool vbSendToLog = true); + + // Methods: +private: + /* ctor */ CMICmnStreamStderr(); + /* ctor */ CMICmnStreamStderr(const CMICmnStreamStderr &); + void operator=(const CMICmnStreamStderr &); + // + bool WritePriv(const CMIUtilString &vText, + const CMIUtilString &vTxtForLogFile, + const bool vbSendToLog = true); + + // Overridden: +private: + // From CMICmnBase + /* dtor */ ~CMICmnStreamStderr() override; + + // Attributes: +private: + CMIUtilThreadMutex m_mutex; // Mutex object for sync during Write() +}; diff --git a/src/MICmnStreamStdin.cpp b/src/MICmnStreamStdin.cpp new file mode 100644 index 00000000000..1c395266ea0 --- /dev/null +++ b/src/MICmnStreamStdin.cpp @@ -0,0 +1,220 @@ +//===-- MICmnStreamStdin.cpp ------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// 2021.3.15 Add support for OHOS target. +// Copyright (c) 2021 Huawei Device Co., Ltd. All rights reserved. +// +//===----------------------------------------------------------------------===// + +// Third Party Headers +#ifdef _WIN32 +#include +#endif +#include + +// In-house headers: +#include "MICmnLog.h" +#include "MICmnResources.h" +#include "MICmnStreamStdin.h" +#include "MICmnStreamStdout.h" +#include "MIDriver.h" +#include "MIUtilSingletonHelper.h" + +//++ +// Details: CMICmnStreamStdin constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnStreamStdin::CMICmnStreamStdin() + : m_strPromptCurrent("(gdb)"), m_bShowPrompt(true), m_pCmdBuffer(nullptr) {} + +//++ +// Details: CMICmnStreamStdin destructor. +// Type: Overridable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnStreamStdin::~CMICmnStreamStdin() { Shutdown(); } + +//++ +// Details: Initialize resources for *this Stdin stream. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnStreamStdin::Initialize() { + m_clientUsageRefCnt++; + + if (m_bInitialized) + return MIstatus::success; + + bool bOk = MIstatus::success; + CMIUtilString errMsg; + + // Note initialisation order is important here as some resources depend on + // previous + MI::ModuleInit(IDS_MI_INIT_ERR_LOG, bOk, errMsg); + MI::ModuleInit(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg); + + if (bOk) { + m_pCmdBuffer = new char[m_constBufferSize]; + } else { + CMIUtilString strInitError(CMIUtilString::Format( + MIRSRC(IDS_MI_INIT_ERR_STREAMSTDIN), errMsg.c_str())); + SetErrorDescription(strInitError); + + return MIstatus::failure; + } + m_bInitialized = bOk; + + return MIstatus::success; +} + +//++ +// Details: Release resources for *this Stdin stream. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnStreamStdin::Shutdown() { + if (--m_clientUsageRefCnt > 0) + return MIstatus::success; + + if (!m_bInitialized) + return MIstatus::success; + + m_bInitialized = false; + + ClrErrorDescription(); + + if (m_pCmdBuffer != nullptr) { + delete[] m_pCmdBuffer; + m_pCmdBuffer = nullptr; + } + + bool bOk = MIstatus::success; + CMIUtilString errMsg; + + MI::ModuleShutdown(IDE_MI_SHTDWN_ERR_RESOURCES, bOk, errMsg); + MI::ModuleShutdown(IDS_MI_SHTDWN_ERR_LOG, bOk, errMsg); + + if (!bOk) { + SetErrorDescriptionn(MIRSRC(IDE_MI_SHTDWN_ERR_STREAMSTDIN), errMsg.c_str()); + } + + return MIstatus::success; +} + +//++ +// Details: Validate and set the text that forms the prompt on the command line. +// Type: Method. +// Args: vNewPrompt - (R) Text description. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnStreamStdin::SetPrompt(const CMIUtilString &vNewPrompt) { + if (vNewPrompt.empty()) { + const CMIUtilString msg(CMIUtilString::Format( + MIRSRC(IDS_STDIN_ERR_INVALID_PROMPT), vNewPrompt.c_str())); + CMICmnStreamStdout::Instance().Write(msg); + return MIstatus::failure; + } + + m_strPromptCurrent = vNewPrompt; + + return MIstatus::success; +} + +//++ +// Details: Retrieve the command line prompt text currently being used. +// Type: Method. +// Args: None. +// Return: const CMIUtilString & - Functional failed. +// Throws: None. +//-- +const CMIUtilString &CMICmnStreamStdin::GetPrompt() const { + return m_strPromptCurrent; +} + +//++ +// Details: Set whether to display optional command line prompt. The prompt is +// output to +// stdout. Disable it when this may interfere with the client reading +// stdout as +// input and it tries to interpret the prompt text to. +// Type: Method. +// Args: vbYes - (R) True = Yes prompt is shown/output to the user +// (stdout), false = no prompt. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +void CMICmnStreamStdin::SetEnablePrompt(const bool vbYes) { + m_bShowPrompt = vbYes; +} + +//++ +// Details: Get whether to display optional command line prompt. The prompt is +// output to +// stdout. Disable it when this may interfere with the client reading +// stdout as +// input and it tries to interpret the prompt text to. +// Type: Method. +// Args: None. +// Return: bool - True = Yes prompt is shown/output to the user (stdout), false +// = no prompt. +// Throws: None. +//-- +bool CMICmnStreamStdin::GetEnablePrompt() const { return m_bShowPrompt; } + +//++ +// Details: Wait on new line of data from stdin stream (completed by '\n' or +// '\r'). +// Type: Method. +// Args: vwErrMsg - (W) Empty string ok or error description. +// Return: char * - text buffer pointer or NULL on failure. +// Throws: None. +//-- +const char *CMICmnStreamStdin::ReadLine(CMIUtilString &vwErrMsg) { + vwErrMsg.clear(); + + // Read user input + const char *pText = ::fgets(&m_pCmdBuffer[0], m_constBufferSize, stdin); + if (pText == nullptr) { +#ifdef _WIN32 + // Was Ctrl-C hit? + // On Windows, Ctrl-C gives an ERROR_OPERATION_ABORTED as error on the + // command-line. + // The end-of-file indicator is also set, so without this check we will exit + // next if statement. + if (::GetLastError() == ERROR_OPERATION_ABORTED) + return nullptr; +#endif + if (::feof(stdin)) { + const bool bForceExit = true; + CMIDriver::Instance().SetExitApplicationFlag(bForceExit); + } else if (::ferror(stdin) != 0) + vwErrMsg = ::strerror(errno); + return nullptr; + } + + // Strip off new line characters + for (char *pI = m_pCmdBuffer; *pI != '\0'; pI++) { + if ((*pI == '\n') || (*pI == '\r')) { + *pI = '\0'; + break; + } + } + + return pText; +} diff --git a/src/MICmnStreamStdin.h b/src/MICmnStreamStdin.h new file mode 100644 index 00000000000..b193757eb74 --- /dev/null +++ b/src/MICmnStreamStdin.h @@ -0,0 +1,60 @@ +//===-- MICmnStreamStdin.h --------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmnBase.h" +#include "MIUtilSingletonBase.h" +#include "MIUtilString.h" +#include "MIUtilThreadBaseStd.h" + +//++ +//============================================================================ +// Details: MI common code class. Used to handle stream data from Stdin. +// Singleton class using the Visitor pattern. A driver using the +// interface +// provide can receive callbacks when a new line of data is received. +// Each line is determined by a carriage return. +// A singleton class. +//-- +class CMICmnStreamStdin : public CMICmnBase, + public MI::ISingleton { + // Give singleton access to private constructors + friend MI::ISingleton; + + // Methods: +public: + bool Initialize() override; + bool Shutdown() override; + // + const CMIUtilString &GetPrompt() const; + bool SetPrompt(const CMIUtilString &vNewPrompt); + void SetEnablePrompt(const bool vbYes); + bool GetEnablePrompt() const; + const char *ReadLine(CMIUtilString &vwErrMsg); + + // Methods: +private: + /* ctor */ CMICmnStreamStdin(); + /* ctor */ CMICmnStreamStdin(const CMICmnStreamStdin &); + void operator=(const CMICmnStreamStdin &); + + // Overridden: +private: + // From CMICmnBase + /* dtor */ ~CMICmnStreamStdin() override; + + // Attributes: +private: + CMIUtilString m_strPromptCurrent; // Command line prompt as shown to the user + bool m_bShowPrompt; // True = Yes prompt is shown/output to the user (stdout), + // false = no prompt + static const int m_constBufferSize = 2048; + char *m_pCmdBuffer; +}; diff --git a/src/MICmnStreamStdout.cpp b/src/MICmnStreamStdout.cpp new file mode 100644 index 00000000000..4a0e2d16adc --- /dev/null +++ b/src/MICmnStreamStdout.cpp @@ -0,0 +1,230 @@ +//===-- MICmnStreamStdout.cpp -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmnStreamStdout.h" +#include "MICmnLog.h" +#include "MICmnResources.h" +#include "MIDriver.h" + +//++ +// Details: CMICmnStreamStdout constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnStreamStdout::CMICmnStreamStdout() {} + +//++ +// Details: CMICmnStreamStdout destructor. +// Type: Overridable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnStreamStdout::~CMICmnStreamStdout() { Shutdown(); } + +//++ +// Details: Initialize resources for *this Stdout stream. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnStreamStdout::Initialize() { + m_clientUsageRefCnt++; + + if (m_bInitialized) + return MIstatus::success; + + bool bOk = MIstatus::success; + +#ifdef _MSC_VER +// Debugging / I/O issues with client. +// This is only required on Windows if you do not use ::flush(stdout). MI uses +// ::flush(stdout) +// It trys to ensure the process attached to the stdout steam gets ALL the data. +//::setbuf( stdout, NULL ); +#endif // _MSC_VER + + m_bInitialized = bOk; + + return MIstatus::success; +} + +//++ +// Details: Release resources for *this Stdout stream. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnStreamStdout::Shutdown() { + if (--m_clientUsageRefCnt > 0) + return MIstatus::success; + + if (!m_bInitialized) + return MIstatus::success; + + ClrErrorDescription(); + + m_bInitialized = false; + + return MIstatus::success; +} + +//++ +// Details: Write an MI format type response to stdout. The text data does not +// need to +// include a carriage line return as this is added to the text. The +// function also +// then passes the text data into the CMICmnLog logger. +// Type: Method. +// Args: vText - (R) MI formatted text. +// vbSendToLog - (R) True = Yes send to the Log file too, false = do +// not. (Dflt = true) +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnStreamStdout::WriteMIResponse(const CMIUtilString &vText, + const bool vbSendToLog /* = true */) { + return WritePriv(vText, vText, vbSendToLog); +} + +//++ +// Details: Write text data to stdout. The text data does not need to +// include a carriage line return as this is added to the text. The +// function also +// then passes the text data into the CMICmnLog logger. +// Type: Method. +// Args: vText - (R) Text data. +// vbSendToLog - (R) True = Yes send to the Log file too, false = do +// not. (Dflt = true) +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnStreamStdout::Write(const CMIUtilString &vText, + const bool vbSendToLog /* = true */) { + if (vText.length() == 0) + return MIstatus::failure; + + const CMIUtilString strPrefixed(CMIUtilString::Format( + "%s: %s", CMIDriver::Instance().GetAppNameShort().c_str(), + vText.c_str())); + + return WritePriv(strPrefixed, vText, vbSendToLog); +} + +//++ +// Details: Write text data to stdout. The text data does not need to +// include a carriage line return as this is added to the text. The +// function also +// then passes the text data into the CMICmnLog logger. +// Type: Method. +// Args: vText - (R) Text data prefixed with MI app's short name. +// vTxtForLogFile - (R) Text data. +// vbSendToLog - (R) True = Yes send to the Log file too, false = +// do not. (Dflt = true) +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnStreamStdout::WritePriv(const CMIUtilString &vText, + const CMIUtilString &vTxtForLogFile, + const bool vbSendToLog /* = true */) { + if (vText.length() == 0) + return MIstatus::failure; + + bool bOk = MIstatus::success; + { + // Grab the stdout thread lock while we print + CMIUtilThreadLock _lock(m_mutex); + + // Send this text to stdout + const MIint status = ::fputs(vText.c_str(), stdout); + if (status == EOF) + // Don't call the CMICmnBase::SetErrorDescription() because it will cause + // a stack overflow: + // CMICmnBase::SetErrorDescription -> CMICmnStreamStdout::Write -> + // CMICmnStreamStdout::WritePriv -> CMICmnBase::SetErrorDescription + bOk = MIstatus::failure; + else { + ::fprintf(stdout, "\n"); + ::fflush(stdout); + } + + // Send this text to the log + if (bOk && vbSendToLog) + bOk &= m_pLog->WriteLog(vTxtForLogFile); + } + + return bOk; +} + +//++ +// Details: Lock the availability of the stream stdout. Other users of *this +// stream will +// be stalled until it is available (Unlock()). +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnStreamStdout::Lock() { + m_mutex.Lock(); + return MIstatus::success; +} + +//++ +// Details: Release a previously locked stdout. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnStreamStdout::Unlock() { + m_mutex.Unlock(); + return MIstatus::success; +} + +//++ +// Details: Take a text data and send to the stdout stream. Also output to the +// MI Log +// file. +// Type: Static method. +// Args: vrTxt - (R) Text. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMICmnStreamStdout::TextToStdout(const CMIUtilString &vrTxt) { + const bool bSendToLog = true; + return CMICmnStreamStdout::Instance().WriteMIResponse(vrTxt, bSendToLog); +} + +//++ +// Details: Write prompt to stdout if it's enabled. +// Type: Static method. +// Args: None. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMICmnStreamStdout::WritePrompt() { + const CMICmnStreamStdin &rStdinMan = CMICmnStreamStdin::Instance(); + if (rStdinMan.GetEnablePrompt()) + return TextToStdout(rStdinMan.GetPrompt()); + return MIstatus::success; +} diff --git a/src/MICmnStreamStdout.h b/src/MICmnStreamStdout.h new file mode 100644 index 00000000000..f73b56ca2ee --- /dev/null +++ b/src/MICmnStreamStdout.h @@ -0,0 +1,62 @@ +//===-- MICmnStreamStdout.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmnBase.h" +#include "MIUtilSingletonBase.h" +#include "MIUtilString.h" +#include "MIUtilThreadBaseStd.h" + +//++ +//============================================================================ +// Details: MI common code class. The MI driver requires this object. +// CMICmnStreamStdout sets up and tears downs stdout for the driver. +// +// Singleton class. +//-- +class CMICmnStreamStdout : public CMICmnBase, + public MI::ISingleton { + friend class MI::ISingleton; + + // Statics: +public: + static bool TextToStdout(const CMIUtilString &vrTxt); + static bool WritePrompt(); + + // Methods: +public: + bool Initialize() override; + bool Shutdown() override; + // + bool Lock(); + bool Unlock(); + bool Write(const CMIUtilString &vText, const bool vbSendToLog = true); + bool WriteMIResponse(const CMIUtilString &vText, + const bool vbSendToLog = true); + + // Methods: +private: + /* ctor */ CMICmnStreamStdout(); + /* ctor */ CMICmnStreamStdout(const CMICmnStreamStdout &); + void operator=(const CMICmnStreamStdout &); + // + bool WritePriv(const CMIUtilString &vText, + const CMIUtilString &vTxtForLogFile, + const bool vbSendToLog = true); + + // Overridden: +private: + // From CMICmnBase + /* dtor */ ~CMICmnStreamStdout() override; + + // Attributes: +private: + CMIUtilThreadMutex m_mutex; // Mutex object for sync during writing to stream +}; diff --git a/src/MICmnThreadMgrStd.cpp b/src/MICmnThreadMgrStd.cpp new file mode 100644 index 00000000000..25afbbcb5d1 --- /dev/null +++ b/src/MICmnThreadMgrStd.cpp @@ -0,0 +1,145 @@ +//===-- MICmnThreadMgrStd.cpp -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MICmnThreadMgrStd.h" +#include "MICmnLog.h" +#include "MICmnResources.h" +#include "MIUtilSingletonHelper.h" + +//++ +// Details: CMICmnThreadMgr constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnThreadMgrStd::CMICmnThreadMgrStd() {} + +//++ +// Details: CMICmnThreadMgr destructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMICmnThreadMgrStd::~CMICmnThreadMgrStd() { Shutdown(); } + +//++ +// Details: Initialise resources for *this thread manager. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnThreadMgrStd::Initialize() { + m_clientUsageRefCnt++; + + if (m_bInitialized) + return MIstatus::success; + + bool bOk = MIstatus::success; + + ClrErrorDescription(); + CMIUtilString errMsg; + + // Note initialisation order is important here as some resources depend on + // previous + MI::ModuleInit(IDS_MI_INIT_ERR_LOG, bOk, errMsg); + MI::ModuleInit(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg); + + m_bInitialized = bOk; + + if (!bOk) { + CMIUtilString strInitError(CMIUtilString::Format( + MIRSRC(IDS_MI_INIT_ERR_THREADMGR), errMsg.c_str())); + SetErrorDescription(strInitError); + return MIstatus::failure; + } + + return bOk; +} + +//++ +// Details: Release resources for *this thread manager. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnThreadMgrStd::Shutdown() { + if (--m_clientUsageRefCnt > 0) + return MIstatus::success; + + if (!m_bInitialized) + return MIstatus::success; + + m_bInitialized = false; + + ClrErrorDescription(); + + bool bOk = MIstatus::success; + CMIUtilString errMsg; + + // Tidy up + ThreadAllTerminate(); + + // Note shutdown order is important here + MI::ModuleShutdown(IDE_MI_SHTDWN_ERR_RESOURCES, bOk, errMsg); + MI::ModuleShutdown(IDS_MI_SHTDWN_ERR_LOG, bOk, errMsg); + + if (!bOk) { + SetErrorDescriptionn(MIRSRC(IDS_MI_SHUTDOWN_ERR), errMsg.c_str()); + } + + return bOk; +} + +//++ +// Details: Ask the thread manager to kill all threads and wait until they have +// died +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnThreadMgrStd::ThreadAllTerminate() { + ThreadList_t::const_iterator it = m_threadList.begin(); + for (; it != m_threadList.end(); ++it) { + // If the thread is still running + CMIUtilThreadActiveObjBase *pThread = *it; + if (pThread->ThreadIsActive()) { + // Ask this thread to kill itself + pThread->ThreadKill(); + + // Wait for this thread to die + pThread->ThreadJoin(); + } + } + + return MIstatus::success; +} + +//++ +// Details: Add a thread object to *this manager's list of thread objects. The +// list to +// used to manage thread objects centrally. +// Type: Method. +// Args: vrObj - (R) A thread object. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMICmnThreadMgrStd::AddThread(const CMIUtilThreadActiveObjBase &vrObj) { + m_threadList.push_back(const_cast(&vrObj)); + + return MIstatus::success; +} diff --git a/src/MICmnThreadMgrStd.h b/src/MICmnThreadMgrStd.h new file mode 100644 index 00000000000..ce8dd70525f --- /dev/null +++ b/src/MICmnThreadMgrStd.h @@ -0,0 +1,123 @@ +//===-- MICmnThreadMgrStd.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// Third party headers: +#include + +// In-house headers: +#include "MICmnBase.h" +#include "MICmnResources.h" +#include "MIUtilSingletonBase.h" +#include "MIUtilThreadBaseStd.h" + +//++ +//============================================================================ +// Details: MI's worker thread (active thread) manager. +// The manager creates threads and behalf of clients. Client are +// responsible for their threads and can delete them when necessary. +// This manager will stop and delete all threads on *this manager's +// shutdown. +// Singleton class. +//-- +class CMICmnThreadMgrStd : public CMICmnBase, + public MI::ISingleton { + friend MI::ISingleton; + + // Methods: +public: + bool Initialize() override; + bool Shutdown() override; + bool ThreadAllTerminate(); // Ask all threads to stop (caution) + template // Ask the thread manager to start and stop threads on + // our behalf + bool ThreadStart(T &vrwObject); + + // Typedef: +private: + typedef std::vector ThreadList_t; + + // Methods: +private: + /* ctor */ CMICmnThreadMgrStd(); + /* ctor */ CMICmnThreadMgrStd(const CMICmnThreadMgrStd &); + void operator=(const CMICmnThreadMgrStd &); + // + bool AddThread(const CMIUtilThreadActiveObjBase & + vrObj); // Add a thread for monitoring by the threadmanager + + // Overridden: +private: + // From CMICmnBase + /* dtor */ ~CMICmnThreadMgrStd() override; + + // Attributes: +private: + CMIUtilThreadMutex m_mutex; + ThreadList_t m_threadList; +}; + +//++ +// Details: Given a thread object start its (worker) thread to do work. The +// object is +// added to the *this manager for housekeeping and deletion of all +// thread objects. +// Type: Template method. +// Args: vrwThreadObj - (RW) A CMIUtilThreadActiveObjBase derived +// object. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +template bool CMICmnThreadMgrStd::ThreadStart(T &vrwThreadObj) { + bool bOk = MIstatus::success; + + // Grab a reference to the base object type + CMIUtilThreadActiveObjBase &rObj = + static_cast(vrwThreadObj); + + // Add to the thread managers internal database + bOk &= AddThread(rObj); + if (!bOk) { + const CMIUtilString errMsg( + CMIUtilString::Format(MIRSRC(IDS_THREADMGR_ERR_THREAD_FAIL_CREATE), + vrwThreadObj.ThreadGetName().c_str())); + SetErrorDescription(errMsg); + return MIstatus::failure; + } + + // Grab a reference on behalf of the caller + bOk &= vrwThreadObj.Acquire(); + if (!bOk) { + const CMIUtilString errMsg( + CMIUtilString::Format(MIRSRC(IDS_THREADMGR_ERR_THREAD_FAIL_CREATE), + vrwThreadObj.ThreadGetName().c_str())); + SetErrorDescription(errMsg); + return MIstatus::failure; + } + + // Thread is already started + // This call must come after the reference count increment + if (vrwThreadObj.ThreadIsActive()) { + // Early exit on thread already running condition + return MIstatus::success; + } + + // Start the thread running + bOk &= vrwThreadObj.ThreadExecute(); + if (!bOk) { + const CMIUtilString errMsg( + CMIUtilString::Format(MIRSRC(IDS_THREADMGR_ERR_THREAD_FAIL_CREATE), + vrwThreadObj.ThreadGetName().c_str())); + SetErrorDescription(errMsg); + return MIstatus::failure; + } + + return MIstatus::success; +} diff --git a/src/MIDataTypes.h b/src/MIDataTypes.h new file mode 100644 index 00000000000..f1d0017a611 --- /dev/null +++ b/src/MIDataTypes.h @@ -0,0 +1,65 @@ +//===-- MIDataTypes.h -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// 2021.3.15 Add support for OHOS target. +// Copyright (c) 2021 Huawei Device Co., Ltd. All rights reserved. +// +//===----------------------------------------------------------------------===// + +// Overview: Common global switches, macros, etc. +// +// This file contains common data types required by applications +// generally. If supported by the compiler, this file should be +// #include'd as part of the project's PCH (precompiled header). + +#pragma once + +// Windows headers: +#ifdef _WIN32 + +// Debugging: +#ifdef _DEBUG +#include +#endif // _DEBUG + +#endif // _WIN32 + +// Common definitions: + +// Function return status +namespace MIstatus { +const bool success = true; +const bool failure = false; +} + +// Use to avoid "unused parameter" compiler warnings: +#define MIunused(x) (void)x; + +// Portability issues +#ifdef _WIN64 +#ifndef _MSC_VER +#define __int64 long long +#endif +typedef unsigned __int64 size_t; +typedef __int64 MIint; +typedef unsigned __int64 MIuint; +#else +#ifdef _WIN32 +typedef unsigned int size_t; +typedef int MIint; +typedef unsigned int MIuint; +#else +typedef int MIint; +typedef unsigned int MIuint; + +#define MAX_PATH 4096 +#endif // _WIN32 +#endif // _WIN64 + +// Common types: + +// Fundamentals: +typedef long long MIint64; // 64bit signed integer. +typedef unsigned long long MIuint64; // 64bit unsigned integer. diff --git a/src/MIDriver.cpp b/src/MIDriver.cpp new file mode 100644 index 00000000000..3bf888e303d --- /dev/null +++ b/src/MIDriver.cpp @@ -0,0 +1,1318 @@ +//===-- MIDriver.cpp --------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Third party headers: +#include "lldb/API/SBError.h" +#include +#include +#include + +// In-house headers: +#include "MICmdArgValFile.h" +#include "MICmdArgValString.h" +#include "MICmdMgr.h" +#include "MICmnConfig.h" +#include "MICmnLLDBDebugSessionInfo.h" +#include "MICmnLLDBDebugger.h" +#include "MICmnLog.h" +#include "MICmnMIResultRecord.h" +#include "MICmnMIValueConst.h" +#include "MICmnResources.h" +#include "MICmnStreamStderr.h" +#include "MICmnStreamStdout.h" +#include "MICmnThreadMgrStd.h" +#include "MIDriver.h" +#include "MIUtilDebug.h" +#include "MIUtilSingletonHelper.h" + +// Instantiations: +#if _DEBUG +const CMIUtilString CMIDriver::ms_constMIVersion = + MIRSRC(IDS_MI_VERSION_DESCRIPTION_DEBUG); +#else +const CMIUtilString CMIDriver::ms_constMIVersion = + MIRSRC(IDS_MI_VERSION_DESCRIPTION); // Matches version in resources file +#endif // _DEBUG +const CMIUtilString + CMIDriver::ms_constAppNameShort(MIRSRC(IDS_MI_APPNAME_SHORT)); +const CMIUtilString CMIDriver::ms_constAppNameLong(MIRSRC(IDS_MI_APPNAME_LONG)); + +//++ +// Details: CMIDriver constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIDriver::CMIDriver() + : m_bFallThruToOtherDriverEnabled(false), m_bDriverIsExiting(false), + m_handleMainThread(nullptr), m_rStdin(CMICmnStreamStdin::Instance()), + m_rLldbDebugger(CMICmnLLDBDebugger::Instance()), + m_rStdOut(CMICmnStreamStdout::Instance()), + m_eCurrentDriverState(eDriverState_NotRunning), + m_bHaveExecutableFileNamePathOnCmdLine(false), + m_bDriverDebuggingArgExecutable(false), + m_bHaveCommandFileNamePathOnCmdLine(false) {} + +//++ +// Details: CMIDriver destructor. +// Type: Overridden. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIDriver::~CMIDriver() {} + +//++ +// Details: Set whether *this driver (the parent) is enabled to pass a command +// to its +// fall through (child) driver to interpret the command and do work +// instead +// (if *this driver decides it can't handle the command). +// Type: Method. +// Args: vbYes - (R) True = yes fall through, false = do not pass on +// command. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriver::SetEnableFallThru(const bool vbYes) { + m_bFallThruToOtherDriverEnabled = vbYes; + return MIstatus::success; +} + +//++ +// Details: Get whether *this driver (the parent) is enabled to pass a command +// to its +// fall through (child) driver to interpret the command and do work +// instead +// (if *this driver decides it can't handle the command). +// Type: Method. +// Args: None. +// Return: bool - True = yes fall through, false = do not pass on command. +// Throws: None. +//-- +bool CMIDriver::GetEnableFallThru() const { + return m_bFallThruToOtherDriverEnabled; +} + +//++ +// Details: Retrieve MI's application name of itself. +// Type: Method. +// Args: None. +// Return: CMIUtilString & - Text description. +// Throws: None. +//-- +const CMIUtilString &CMIDriver::GetAppNameShort() const { + return ms_constAppNameShort; +} + +//++ +// Details: Retrieve MI's application name of itself. +// Type: Method. +// Args: None. +// Return: CMIUtilString & - Text description. +// Throws: None. +//-- +const CMIUtilString &CMIDriver::GetAppNameLong() const { + return ms_constAppNameLong; +} + +//++ +// Details: Retrieve MI's version description of itself. +// Type: Method. +// Args: None. +// Return: CMIUtilString & - Text description. +// Throws: None. +//-- +const CMIUtilString &CMIDriver::GetVersionDescription() const { + return ms_constMIVersion; +} + +//++ +// Details: Initialize setup *this driver ready for use. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriver::Initialize() { + m_eCurrentDriverState = eDriverState_Initialising; + m_clientUsageRefCnt++; + + ClrErrorDescription(); + + if (m_bInitialized) + return MIstatus::success; + + bool bOk = MIstatus::success; + CMIUtilString errMsg; + + // Initialize all of the modules we depend on + MI::ModuleInit(IDS_MI_INIT_ERR_LOG, bOk, errMsg); + MI::ModuleInit(IDS_MI_INIT_ERR_STREAMSTDOUT, bOk, errMsg); + MI::ModuleInit(IDS_MI_INIT_ERR_STREAMSTDERR, bOk, errMsg); + MI::ModuleInit(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg); + MI::ModuleInit(IDS_MI_INIT_ERR_THREADMANAGER, bOk, + errMsg); + MI::ModuleInit(IDS_MI_INIT_ERR_STREAMSTDIN, bOk, errMsg); + MI::ModuleInit(IDS_MI_INIT_ERR_CMDMGR, bOk, errMsg); + bOk &= m_rLldbDebugger.SetDriver(*this); + MI::ModuleInit(IDS_MI_INIT_ERR_LLDBDEBUGGER, bOk, errMsg); + + m_bExitApp = false; + + m_bInitialized = bOk; + + if (!bOk) { + const CMIUtilString msg = + CMIUtilString::Format(MIRSRC(IDS_MI_INIT_ERR_DRIVER), errMsg.c_str()); + SetErrorDescription(msg); + return MIstatus::failure; + } + + m_eCurrentDriverState = eDriverState_RunningNotDebugging; + + return bOk; +} + +//++ +// Details: Unbind detach or release resources used by *this driver. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriver::Shutdown() { + if (--m_clientUsageRefCnt > 0) + return MIstatus::success; + + if (!m_bInitialized) + return MIstatus::success; + + m_eCurrentDriverState = eDriverState_ShuttingDown; + + ClrErrorDescription(); + + bool bOk = MIstatus::success; + CMIUtilString errMsg; + + // Shutdown all of the modules we depend on + MI::ModuleShutdown(IDS_MI_INIT_ERR_LLDBDEBUGGER, bOk, + errMsg); + MI::ModuleShutdown(IDS_MI_INIT_ERR_CMDMGR, bOk, errMsg); + MI::ModuleShutdown(IDS_MI_INIT_ERR_STREAMSTDIN, bOk, + errMsg); + MI::ModuleShutdown(IDS_MI_INIT_ERR_THREADMANAGER, bOk, + errMsg); + MI::ModuleShutdown(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg); + MI::ModuleShutdown(IDS_MI_INIT_ERR_STREAMSTDERR, bOk, + errMsg); + MI::ModuleShutdown(IDS_MI_INIT_ERR_STREAMSTDOUT, bOk, + errMsg); + MI::ModuleShutdown(IDS_MI_INIT_ERR_LOG, bOk, errMsg); + + if (!bOk) { + SetErrorDescriptionn(MIRSRC(IDS_MI_SHUTDOWN_ERR), errMsg.c_str()); + } + + m_eCurrentDriverState = eDriverState_NotRunning; + + return bOk; +} + +//++ +// Details: Work function. Client (the driver's user) is able to append their +// own message +// in to the MI's Log trace file. +// Type: Method. +// Args: vMessage - (R) Client's text message. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriver::WriteMessageToLog(const CMIUtilString &vMessage) { + CMIUtilString msg; + msg = CMIUtilString::Format(MIRSRC(IDS_MI_CLIENT_MSG), vMessage.c_str()); + return m_pLog->Write(msg, CMICmnLog::eLogVerbosity_ClientMsg); +} + +//++ +// Details: CDriverMgr calls *this driver initialize setup ready for use. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriver::DoInitialize() { return CMIDriver::Instance().Initialize(); } + +//++ +// Details: CDriverMgr calls *this driver to unbind detach or release resources +// used by +// *this driver. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriver::DoShutdown() { return CMIDriver::Instance().Shutdown(); } + +//++ +// Details: Retrieve the name for *this driver. +// Type: Overridden. +// Args: None. +// Return: CMIUtilString & - Driver name. +// Throws: None. +//-- +const CMIUtilString &CMIDriver::GetName() const { + const CMIUtilString &rName = GetAppNameLong(); + const CMIUtilString &rVsn = GetVersionDescription(); + static CMIUtilString strName = + CMIUtilString::Format("%s %s", rName.c_str(), rVsn.c_str()); + + return strName; +} + +//++ +// Details: Retrieve *this driver's last error condition. +// Type: Overridden. +// Args: None. +// Return: CMIUtilString - Text description. +// Throws: None. +//-- +CMIUtilString CMIDriver::GetError() const { return GetErrorDescription(); } + +//++ +// Details: Call *this driver to return it's debugger. +// Type: Overridden. +// Args: None. +// Return: lldb::SBDebugger & - LLDB debugger object reference. +// Throws: None. +//-- +lldb::SBDebugger &CMIDriver::GetTheDebugger() { + return m_rLldbDebugger.GetTheDebugger(); +} + +//++ +// Details: Specify another driver *this driver can call should this driver not +// be able +// to handle the client data input. DoFallThruToAnotherDriver() makes +// the call. +// Type: Overridden. +// Args: vrOtherDriver - (R) Reference to another driver object. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriver::SetDriverToFallThruTo(const CMIDriverBase &vrOtherDriver) { + m_pDriverFallThru = const_cast(&vrOtherDriver); + + return m_pDriverFallThru->SetDriverParent(*this); +} + +//++ +// Details: Proxy function CMIDriverMgr IDriver interface implementation. *this +// driver's +// implementation called from here to match the existing function name +// of the +// original LLDB driver class (the extra indirection is not necessarily +// required). +// Check the arguments that were passed to this program to make sure +// they are +// valid and to get their argument values (if any). +// Type: Overridden. +// Args: argc - (R) An integer that contains the count of arguments +// that follow in +// argv. The argc parameter is always greater than +// or equal to 1. +// argv - (R) An array of null-terminated strings representing +// command-line +// arguments entered by the user of the program. By +// convention, +// argv[0] is the command with which the program is +// invoked. +// vpStdOut - (R) Pointer to a standard output stream. +// vwbExiting - (W) True = *this want to exit, Reasons: help, +// invalid arg(s), +// version information only. +// False = Continue to work, start debugger i.e. +// Command +// interpreter. +// Return: lldb::SBError - LLDB current error status. +// Throws: None. +//-- +lldb::SBError CMIDriver::DoParseArgs(const int argc, const char *argv[], + FILE *vpStdOut, bool &vwbExiting) { + return ParseArgs(argc, argv, vpStdOut, vwbExiting); +} + +//++ +// Details: Check the arguments that were passed to this program to make sure +// they are +// valid and to get their argument values (if any). The following are +// options +// that are only handled by *this driver: +// --executable +// --source or -s +// --synchronous +// The application's options --interpreter and --executable in code act +// very similar. +// The --executable is necessary to differentiate whether the MI Driver +// is being +// used by a client (e.g. Eclipse) or from the command line. Eclipse +// issues the option +// --interpreter and also passes additional arguments which can be +// interpreted as an +// executable if called from the command line. Using --executable tells +// the MI Driver +// it is being called from the command line and to prepare to launch +// the executable +// argument for a debug session. Using --interpreter on the command +// line does not +// issue additional commands to initialise a debug session. +// Option --synchronous disables an asynchronous mode in the lldb-mi driver. +// Type: Overridden. +// Args: argc - (R) An integer that contains the count of arguments +// that follow in +// argv. The argc parameter is always greater than +// or equal to 1. +// argv - (R) An array of null-terminated strings representing +// command-line +// arguments entered by the user of the program. By +// convention, +// argv[0] is the command with which the program is +// invoked. +// vpStdOut - (R) Pointer to a standard output stream. +// vwbExiting - (W) True = *this want to exit, Reasons: help, +// invalid arg(s), +// version information only. +// False = Continue to work, start debugger i.e. +// Command +// interpreter. +// Return: lldb::SBError - LLDB current error status. +// Throws: None. +//-- +lldb::SBError CMIDriver::ParseArgs(const int argc, const char *argv[], + FILE *vpStdOut, bool &vwbExiting) { + lldb::SBError errStatus; + const bool bHaveArgs(argc >= 2); + + // *** Add any args handled here to GetHelpOnCmdLineArgOptions() *** + + // CODETAG_MIDRIVE_CMD_LINE_ARG_HANDLING + // Look for the command line options + bool bHaveExecutableFileNamePath = false; + bool bHaveExecutableLongOption = false; + + if (bHaveArgs) { + // Search right to left to look for filenames + for (MIint i = argc - 1; i > 0; i--) { + const CMIUtilString strArg(argv[i]); + const CMICmdArgValFile argFile; + + // Check for a filename + if (argFile.IsFilePath(strArg) || + CMICmdArgValString(true, false, true).IsStringArg(strArg)) { + // Is this the command file for the '-s' or '--source' options? + const CMIUtilString strPrevArg(argv[i - 1]); + if (strPrevArg == "-s" || strPrevArg == "--source") { + m_strCmdLineArgCommandFileNamePath = strArg; + m_bHaveCommandFileNamePathOnCmdLine = true; + i--; // skip '-s' on the next loop + continue; + } + // Else, must be the executable + bHaveExecutableFileNamePath = true; + m_strCmdLineArgExecuteableFileNamePath = strArg; + m_bHaveExecutableFileNamePathOnCmdLine = true; + } + // Report error if no command file was specified for the '-s' or + // '--source' options + else if (strArg == "-s" || strArg == "--source") { + vwbExiting = true; + const CMIUtilString errMsg = CMIUtilString::Format( + MIRSRC(IDS_CMD_ARGS_ERR_VALIDATION_MISSING_INF), strArg.c_str()); + errStatus.SetErrorString(errMsg.c_str()); + break; + } + // This argument is also checked for in CMIDriverMgr::ParseArgs() + else if (strArg == "--executable") // Used to specify that + // there is executable + // argument also on the + // command line + { // See fn description. + bHaveExecutableLongOption = true; + } else if (strArg == "--synchronous") { + CMICmnLLDBDebugSessionInfo::Instance().GetDebugger().SetAsync(false); + } + } + } + + if (bHaveExecutableFileNamePath && bHaveExecutableLongOption) { + SetDriverDebuggingArgExecutable(); + } + + return errStatus; +} + +//++ +// Details: A client can ask if *this driver is GDB/MI compatible. +// Type: Overridden. +// Args: None. +// Return: True - GBD/MI compatible LLDB front end. +// False - Not GBD/MI compatible LLDB front end. +// Throws: None. +//-- +bool CMIDriver::GetDriverIsGDBMICompatibleDriver() const { return true; } + +//++ +// Details: Start worker threads for the driver. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriver::StartWorkerThreads() { + bool bOk = MIstatus::success; + + // Grab the thread manager + CMICmnThreadMgrStd &rThreadMgr = CMICmnThreadMgrStd::Instance(); + + // Start the event polling thread + if (bOk && !rThreadMgr.ThreadStart(m_rLldbDebugger)) { + const CMIUtilString errMsg = CMIUtilString::Format( + MIRSRC(IDS_THREADMGR_ERR_THREAD_FAIL_CREATE), + CMICmnThreadMgrStd::Instance().GetErrorDescription().c_str()); + SetErrorDescription(errMsg); + return MIstatus::failure; + } + + return bOk; +} + +//++ +// Details: Stop worker threads for the driver. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriver::StopWorkerThreads() { + CMICmnThreadMgrStd &rThreadMgr = CMICmnThreadMgrStd::Instance(); + return rThreadMgr.ThreadAllTerminate(); +} + +//++ +// Details: Call this function puts *this driver to work. +// This function is used by the application's main thread. +// Type: Overridden. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriver::DoMainLoop() { + if (!InitClientIDEToMIDriver()) // Init Eclipse IDE + { + SetErrorDescriptionn(MIRSRC(IDS_MI_INIT_ERR_CLIENT_USING_DRIVER)); + return MIstatus::failure; + } + + if (!StartWorkerThreads()) + return MIstatus::failure; + + bool bOk = MIstatus::success; + + if (HaveExecutableFileNamePathOnCmdLine()) { + if (!LocalDebugSessionStartupExecuteCommands()) { + SetErrorDescription(MIRSRC(IDS_MI_INIT_ERR_LOCAL_DEBUG_SESSION)); + bOk = MIstatus::failure; + } + } + + // App is not quitting currently + m_bExitApp = false; + + // Handle source file + if (m_bHaveCommandFileNamePathOnCmdLine) { + const bool bAsyncMode = false; + ExecuteCommandFile(bAsyncMode); + } + + // While the app is active + while (bOk && !m_bExitApp) { + CMIUtilString errorText; + const char *pCmd = m_rStdin.ReadLine(errorText); + if (pCmd != nullptr) { + CMIUtilString lineText(pCmd); + if (!lineText.empty()) { + // Check that the handler thread is alive (otherwise we stuck here) + assert(CMICmnLLDBDebugger::Instance().ThreadIsActive()); + + { + // Lock Mutex before processing commands so that we don't disturb an + // event + // being processed + CMIUtilThreadLock lock( + CMICmnLLDBDebugSessionInfo::Instance().GetSessionMutex()); + bOk = InterpretCommand(lineText); + } + + // Draw prompt if desired + bOk = bOk && CMICmnStreamStdout::WritePrompt(); + + // Wait while the handler thread handles incoming events + CMICmnLLDBDebugger::Instance().WaitForHandleEvent(); + } + } + } + + // Signal that the application is shutting down + DoAppQuit(); + + // Close and wait for the workers to stop + StopWorkerThreads(); + + return MIstatus::success; +} + +//++ +// Details: Set things in motion, set state etc that brings *this driver (and +// the +// application) to a tidy shutdown. +// This function is used by the application's main thread. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriver::DoAppQuit() { + bool bYesQuit = true; + + // Shutdown stuff, ready app for exit + { + CMIUtilThreadLock lock(m_threadMutex); + m_bDriverIsExiting = true; + } + + return bYesQuit; +} + +//++ +// Details: *this driver passes text commands to a fall through driver is it +// does not +// understand them (the LLDB driver). +// This function is used by the application's main thread. +// Type: Method. +// Args: vTextLine - (R) Text data representing a possible command. +// vwbCmdYesValid - (W) True = Command valid, false = command not +// handled. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriver::InterpretCommandFallThruDriver(const CMIUtilString &vTextLine, + bool &vwbCmdYesValid) { + MIunused(vTextLine); + MIunused(vwbCmdYesValid); + + // ToDo: Implement when less urgent work to be done or decide remove as not + // required + // bool bOk = MIstatus::success; + // bool bCmdNotUnderstood = true; + // if( bCmdNotUnderstood && GetEnableFallThru() ) + //{ + // CMIUtilString errMsg; + // bOk = DoFallThruToAnotherDriver( vStdInBuffer, errMsg ); + // if( !bOk ) + // { + // errMsg = errMsg.StripCREndOfLine(); + // errMsg = errMsg.StripCRAll(); + // const CMIDriverBase * pOtherDriver = GetDriverToFallThruTo(); + // const char * pName = pOtherDriver->GetDriverName().c_str(); + // const char * pId = pOtherDriver->GetDriverId().c_str(); + // const CMIUtilString msg( CMIUtilString::Format( MIRSRC( + // IDS_DRIVER_ERR_FALLTHRU_DRIVER_ERR ), pName, pId, errMsg.c_str() ) + //); + // m_pLog->WriteMsg( msg ); + // } + //} + // + // vwbCmdYesValid = bOk; + // CMIUtilString strNot; + // if( vwbCmdYesValid) + // strNot = CMIUtilString::Format( "%s ", MIRSRC( IDS_WORD_NOT ) ); + // const CMIUtilString msg( CMIUtilString::Format( MIRSRC( + // IDS_FALLTHRU_DRIVER_CMD_RECEIVED ), vTextLine.c_str(), strNot.c_str() ) ); + // m_pLog->WriteLog( msg ); + + return MIstatus::success; +} + +//++ +// Details: Retrieve the name for *this driver. +// Type: Overridden. +// Args: None. +// Return: CMIUtilString & - Driver name. +// Throws: None. +//-- +const CMIUtilString &CMIDriver::GetDriverName() const { return GetName(); } + +//++ +// Details: Get the unique ID for *this driver. +// Type: Overridden. +// Args: None. +// Return: CMIUtilString & - Text description. +// Throws: None. +//-- +const CMIUtilString &CMIDriver::GetDriverId() const { return GetId(); } + +//++ +// Details: This function allows *this driver to call on another driver to +// perform work +// should this driver not be able to handle the client data input. +// SetDriverToFallThruTo() specifies the fall through to driver. +// Check the error message if the function returns a failure. +// Type: Overridden. +// Args: vCmd - (R) Command instruction to interpret. +// vwErrMsg - (W) Status description on command failing. +// Return: MIstatus::success - Command succeeded. +// MIstatus::failure - Command failed. +// Throws: None. +//-- +bool CMIDriver::DoFallThruToAnotherDriver(const CMIUtilString &vCmd, + CMIUtilString &vwErrMsg) { + bool bOk = MIstatus::success; + + CMIDriverBase *pOtherDriver = GetDriverToFallThruTo(); + if (pOtherDriver == nullptr) + return bOk; + + return pOtherDriver->DoFallThruToAnotherDriver(vCmd, vwErrMsg); +} + +//++ +// Details: *this driver provides a file stream to other drivers on which *this +// driver +// write's out to and they read as expected input. *this driver is +// passing +// through commands to the (child) pass through assigned driver. +// Type: Overrdidden. +// Args: None. +// Return: FILE * - Pointer to stream. +// Throws: None. +//-- +FILE *CMIDriver::GetStdin() const { + // Note this fn is called on CMIDriverMgr register driver so stream has to be + // available before *this driver has been initialized! Flaw? + + // This very likely to change later to a stream that the pass thru driver + // will read and we write to give it 'input' + return stdin; +} + +//++ +// Details: *this driver provides a file stream to other pass through assigned +// drivers +// so they know what to write to. +// Type: Overidden. +// Args: None. +// Return: FILE * - Pointer to stream. +// Throws: None. +//-- +FILE *CMIDriver::GetStdout() const { + // Note this fn is called on CMIDriverMgr register driver so stream has to be + // available before *this driver has been initialized! Flaw? + + // Do not want to pass through driver to write to stdout + return nullptr; +} + +//++ +// Details: *this driver provides a error file stream to other pass through +// assigned drivers +// so they know what to write to. +// Type: Overidden. +// Args: None. +// Return: FILE * - Pointer to stream. +// Throws: None. +//-- +FILE *CMIDriver::GetStderr() const { + // Note this fn is called on CMIDriverMgr register driver so stream has to be + // available before *this driver has been initialized! Flaw? + + // This very likely to change later to a stream that the pass thru driver + // will write to and *this driver reads from to pass on the CMICmnLog object + return stderr; +} + +//++ +// Details: Set a unique ID for *this driver. It cannot be empty. +// Type: Overridden. +// Args: vId - (R) Text description. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriver::SetId(const CMIUtilString &vId) { + if (vId.empty()) { + SetErrorDescriptionn(MIRSRC(IDS_DRIVER_ERR_ID_INVALID), GetName().c_str(), + vId.c_str()); + return MIstatus::failure; + } + + m_strDriverId = vId; + return MIstatus::success; +} + +//++ +// Details: Get the unique ID for *this driver. +// Type: Overridden. +// Args: None. +// Return: CMIUtilString & - Text description. +// Throws: None. +//-- +const CMIUtilString &CMIDriver::GetId() const { return m_strDriverId; } + +//++ +// Details: Interpret the text data and match against current commands to see if +// there +// is a match. If a match then the command is issued and actioned on. +// The +// text data if not understood by *this driver is past on to the Fall +// Thru +// driver. +// This function is used by the application's main thread. +// Type: Method. +// Args: vTextLine - (R) Text data representing a possible command. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriver::InterpretCommand(const CMIUtilString &vTextLine) { + const bool bNeedToRebroadcastStopEvent = + m_rLldbDebugger.CheckIfNeedToRebroadcastStopEvent(); + bool bCmdYesValid = false; + bool bOk = InterpretCommandThisDriver(vTextLine, bCmdYesValid); + if (bOk && !bCmdYesValid) + bOk = InterpretCommandFallThruDriver(vTextLine, bCmdYesValid); + + if (bNeedToRebroadcastStopEvent) + m_rLldbDebugger.RebroadcastStopEvent(); + + return bOk; +} + +//++ +// Details: Helper function for CMIDriver::InterpretCommandThisDriver. +// Convert a CLI command to MI command (just wrap any CLI command +// into "-interpreter-exec command \"\""). +// Type: Method. +// Args: vTextLine - (R) Text data representing a possible command. +// Return: CMIUtilString - The original MI command or converted CLI command. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +CMIUtilString +CMIDriver::WrapCLICommandIntoMICommand(const CMIUtilString &vTextLine) const { + // Tokens contain following digits + static const CMIUtilString digits("0123456789"); + + // Consider an algorithm on the following example: + // 001-file-exec-and-symbols "/path/to/file" + // + // 1. Skip a command token + // For example: + // 001-file-exec-and-symbols "/path/to/file" + // 001target create "/path/to/file" + // ^ -- command starts here (in both cases) + // Also possible case when command not found: + // 001 + // ^ -- i.e. only tokens are present (or empty string at all) + const size_t nCommandOffset = vTextLine.find_first_not_of(digits); + + // 2. Check if command is empty + // For example: + // 001-file-exec-and-symbols "/path/to/file" + // 001target create "/path/to/file" + // ^ -- command not empty (in both cases) + // or: + // 001 + // ^ -- command wasn't found + const bool bIsEmptyCommand = (nCommandOffset == CMIUtilString::npos); + + // 3. Check and exit if it isn't a CLI command + // For example: + // 001-file-exec-and-symbols "/path/to/file" + // 001 + // ^ -- it isn't CLI command (in both cases) + // or: + // 001target create "/path/to/file" + // ^ -- it's CLI command + const bool bIsCliCommand = + !bIsEmptyCommand && (vTextLine.at(nCommandOffset) != '-'); + if (!bIsCliCommand) + return vTextLine; + + // 4. Wrap CLI command to make it MI-compatible + // + // 001target create "/path/to/file" + // ^^^ -- token + const std::string vToken(vTextLine.begin(), + vTextLine.begin() + nCommandOffset); + // 001target create "/path/to/file" + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- CLI command + const CMIUtilString vCliCommand(std::string(vTextLine, nCommandOffset)); + + // 5. Escape special characters and embed the command in a string + // Result: it looks like -- target create \"/path/to/file\". + const std::string vShieldedCliCommand(vCliCommand.AddSlashes()); + + // 6. Turn the CLI command into an MI command, as in: + // 001-interpreter-exec command "target create \"/path/to/file\"" + // ^^^ -- token + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^ -- wrapper + // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -- shielded + // CLI command + return CMIUtilString::Format("%s-interpreter-exec command \"%s\"", + vToken.c_str(), vShieldedCliCommand.c_str()); +} + +//++ +// Details: Interpret the text data and match against current commands to see if +// there +// is a match. If a match then the command is issued and actioned on. +// If a +// command cannot be found to match then vwbCmdYesValid is set to false +// and +// nothing else is done here. +// This function is used by the application's main thread. +// Type: Method. +// Args: vTextLine - (R) Text data representing a possible command. +// vwbCmdYesValid - (W) True = Command valid, false = command not +// handled. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriver::InterpretCommandThisDriver(const CMIUtilString &vTextLine, + bool &vwbCmdYesValid) { + // Convert any CLI commands into MI commands + const CMIUtilString vMITextLine(WrapCLICommandIntoMICommand(vTextLine)); + + vwbCmdYesValid = false; + bool bCmdNotInCmdFactor = false; + SMICmdData cmdData; + CMICmdMgr &rCmdMgr = CMICmdMgr::Instance(); + if (!rCmdMgr.CmdInterpret(vMITextLine, vwbCmdYesValid, bCmdNotInCmdFactor, + cmdData)) + return MIstatus::failure; + + if (vwbCmdYesValid) { + // For debugging only + // m_pLog->WriteLog( cmdData.strMiCmdAll.c_str() ); + + return ExecuteCommand(cmdData); + } + + // Check for escape character, may be cursor control characters + // This code is not necessary for application operation, just want to keep + // tabs on what + // has been given to the driver to try and interpret. + if (vMITextLine.at(0) == 27) { + CMIUtilString logInput(MIRSRC(IDS_STDIN_INPUT_CTRL_CHARS)); + for (MIuint i = 0; i < vMITextLine.length(); i++) { + logInput += CMIUtilString::Format("%d ", vMITextLine.at(i)); + } + m_pLog->WriteLog(logInput); + return MIstatus::success; + } + + // Write to the Log that a 'command' was not valid. + // Report back to the MI client via MI result record. + CMIUtilString strNotInCmdFactory; + if (bCmdNotInCmdFactor) + strNotInCmdFactory = CMIUtilString::Format( + MIRSRC(IDS_DRIVER_CMD_NOT_IN_FACTORY), cmdData.strMiCmd.c_str()); + const CMIUtilString strNot( + CMIUtilString::Format("%s ", MIRSRC(IDS_WORD_NOT))); + const CMIUtilString msg(CMIUtilString::Format( + MIRSRC(IDS_DRIVER_CMD_RECEIVED), vMITextLine.c_str(), strNot.c_str(), + strNotInCmdFactory.c_str())); + const CMICmnMIValueConst vconst = CMICmnMIValueConst(msg); + const CMICmnMIValueResult valueResult("msg", vconst); + const CMICmnMIResultRecord miResultRecord( + cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Error, + valueResult); + const bool bOk = m_rStdOut.WriteMIResponse(miResultRecord.GetString()); + + // Proceed to wait for or execute next command + return bOk; +} + +//++ +// Details: Having previously had the potential command validated and found +// valid now +// get the command executed. +// This function is used by the application's main thread. +// Type: Method. +// Args: vCmdData - (RW) Command meta data. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriver::ExecuteCommand(const SMICmdData &vCmdData) { + CMICmdMgr &rCmdMgr = CMICmdMgr::Instance(); + return rCmdMgr.CmdExecute(vCmdData); +} + +//++ +// Details: Set the MI Driver's exit application flag. The application checks +// this flag +// after every stdin line is read so the exit may not be instantaneous. +// If vbForceExit is false the MI Driver queries its state and +// determines if is +// should exit or continue operating depending on that running state. +// This is related to the running state of the MI driver. +// Type: Overridden. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMIDriver::SetExitApplicationFlag(const bool vbForceExit) { + if (vbForceExit) { + CMIUtilThreadLock lock(m_threadMutex); + m_bExitApp = true; + return; + } + + // CODETAG_DEBUG_SESSION_RUNNING_PROG_RECEIVED_SIGINT_PAUSE_PROGRAM + // Did we receive a SIGINT from the client during a running debug program, if + // so then SIGINT is not to be taken as meaning kill the MI driver application + // but halt the inferior program being debugged instead + if (m_eCurrentDriverState == eDriverState_RunningDebugging) { + InterpretCommand("-exec-interrupt"); + return; + } + + m_bExitApp = true; +} + +//++ +// Details: Get the MI Driver's exit exit application flag. +// This is related to the running state of the MI driver. +// Type: Method. +// Args: None. +// Return: bool - True = MI Driver is shutting down, false = MI driver is +// running. +// Throws: None. +//-- +bool CMIDriver::GetExitApplicationFlag() const { return m_bExitApp; } + +//++ +// Details: Get the current running state of the MI Driver. +// Type: Method. +// Args: None. +// Return: DriverState_e - The current running state of the application. +// Throws: None. +//-- +CMIDriver::DriverState_e CMIDriver::GetCurrentDriverState() const { + return m_eCurrentDriverState; +} + +//++ +// Details: Set the current running state of the MI Driver to running and +// currently not in +// a debug session. +// Type: Method. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Return: DriverState_e - The current running state of the application. +// Throws: None. +//-- +bool CMIDriver::SetDriverStateRunningNotDebugging() { + // CODETAG_DEBUG_SESSION_RUNNING_PROG_RECEIVED_SIGINT_PAUSE_PROGRAM + + if (m_eCurrentDriverState == eDriverState_RunningNotDebugging) + return MIstatus::success; + + // Driver cannot be in the following states to set + // eDriverState_RunningNotDebugging + switch (m_eCurrentDriverState) { + case eDriverState_NotRunning: + case eDriverState_Initialising: + case eDriverState_ShuttingDown: { + SetErrorDescription(MIRSRC(IDS_DRIVER_ERR_DRIVER_STATE_ERROR)); + return MIstatus::failure; + } + case eDriverState_RunningDebugging: + case eDriverState_RunningNotDebugging: + break; + case eDriverState_count: + SetErrorDescription( + CMIUtilString::Format(MIRSRC(IDS_CODE_ERR_INVALID_ENUMERATION_VALUE), + "SetDriverStateRunningNotDebugging()")); + return MIstatus::failure; + } + + // Driver must be in this state to set eDriverState_RunningNotDebugging + if (m_eCurrentDriverState != eDriverState_RunningDebugging) { + SetErrorDescription(MIRSRC(IDS_DRIVER_ERR_DRIVER_STATE_ERROR)); + return MIstatus::failure; + } + + m_eCurrentDriverState = eDriverState_RunningNotDebugging; + + return MIstatus::success; +} + +//++ +// Details: Set the current running state of the MI Driver to running and +// currently not in +// a debug session. The driver's state must in the state running and in +// a +// debug session to set this new state. +// Type: Method. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Return: DriverState_e - The current running state of the application. +// Throws: None. +//-- +bool CMIDriver::SetDriverStateRunningDebugging() { + // CODETAG_DEBUG_SESSION_RUNNING_PROG_RECEIVED_SIGINT_PAUSE_PROGRAM + + if (m_eCurrentDriverState == eDriverState_RunningDebugging) + return MIstatus::success; + + // Driver cannot be in the following states to set + // eDriverState_RunningDebugging + switch (m_eCurrentDriverState) { + case eDriverState_NotRunning: + case eDriverState_Initialising: + case eDriverState_ShuttingDown: { + SetErrorDescription(MIRSRC(IDS_DRIVER_ERR_DRIVER_STATE_ERROR)); + return MIstatus::failure; + } + case eDriverState_RunningDebugging: + case eDriverState_RunningNotDebugging: + break; + case eDriverState_count: + SetErrorDescription( + CMIUtilString::Format(MIRSRC(IDS_CODE_ERR_INVALID_ENUMERATION_VALUE), + "SetDriverStateRunningDebugging()")); + return MIstatus::failure; + } + + // Driver must be in this state to set eDriverState_RunningDebugging + if (m_eCurrentDriverState != eDriverState_RunningNotDebugging) { + SetErrorDescription(MIRSRC(IDS_DRIVER_ERR_DRIVER_STATE_ERROR)); + return MIstatus::failure; + } + + m_eCurrentDriverState = eDriverState_RunningDebugging; + + return MIstatus::success; +} + +//++ +// Details: Prepare the client IDE so it will start working/communicating with +// *this MI +// driver. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMIDriver::InitClientIDEToMIDriver() const { + // Put other IDE init functions here + return InitClientIDEEclipse(); +} + +//++ +// Details: The IDE Eclipse when debugging locally expects "(gdb)\n" character +// sequence otherwise it refuses to communicate and times out. This +// should be +// sent to Eclipse before anything else. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMIDriver::InitClientIDEEclipse() const { + return CMICmnStreamStdout::WritePrompt(); +} + +//++ +// Details: Ask *this driver whether it found an executable in the MI Driver's +// list of +// arguments which to open and debug. If so instigate commands to set +// up a debug +// session for that executable. +// Type: Method. +// Args: None. +// Return: bool - True = True = Yes executable given as one of the parameters +// to the MI +// Driver. +// False = not found. +// Throws: None. +//-- +bool CMIDriver::HaveExecutableFileNamePathOnCmdLine() const { + return m_bHaveExecutableFileNamePathOnCmdLine; +} + +//++ +// Details: Retrieve from *this driver executable file name path to start a +// debug session +// with (if present see HaveExecutableFileNamePathOnCmdLine()). +// Type: Method. +// Args: None. +// Return: CMIUtilString & - Executeable file name path or empty string. +// Throws: None. +//-- +const CMIUtilString &CMIDriver::GetExecutableFileNamePathOnCmdLine() const { + return m_strCmdLineArgExecuteableFileNamePath; +} + +//++ +// Details: Execute commands (by injecting them into the stdin line queue +// container) and +// other code to set up the MI Driver such that is can take the +// executable +// argument passed on the command and create a debug session for it. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functionality succeeded. +// MIstatus::failure - Functionality failed. +// Throws: None. +//-- +bool CMIDriver::LocalDebugSessionStartupExecuteCommands() { + const CMIUtilString strCmd(CMIUtilString::Format( + "-file-exec-and-symbols \"%s\"", + m_strCmdLineArgExecuteableFileNamePath.AddSlashes().c_str())); + bool bOk = CMICmnStreamStdout::TextToStdout(strCmd); + bOk = bOk && InterpretCommand(strCmd); + bOk = bOk && CMICmnStreamStdout::WritePrompt(); + return bOk; +} + +//++ +// Details: Set the MI Driver into "its debugging an executable passed as an +// argument" +// mode as against running via a client like Eclipse. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMIDriver::SetDriverDebuggingArgExecutable() { + m_bDriverDebuggingArgExecutable = true; +} + +//++ +// Details: Retrieve the MI Driver state indicating if it is operating in "its +// debugging +// an executable passed as an argument" mode as against running via a +// client +// like Eclipse. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +bool CMIDriver::IsDriverDebuggingArgExecutable() const { + return m_bDriverDebuggingArgExecutable; +} + +//++ +// Details: Execute commands from command source file in specified mode, and +// set exit-flag if needed. +// Type: Method. +// Args: vbAsyncMode - (R) True = execute commands in asynchronous +// mode, false = otherwise. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +bool CMIDriver::ExecuteCommandFile(const bool vbAsyncMode) { + std::ifstream ifsStartScript(m_strCmdLineArgCommandFileNamePath.c_str()); + if (!ifsStartScript.is_open()) { + const CMIUtilString errMsg( + CMIUtilString::Format(MIRSRC(IDS_UTIL_FILE_ERR_OPENING_FILE_UNKNOWN), + m_strCmdLineArgCommandFileNamePath.c_str())); + SetErrorDescription(errMsg.c_str()); + const bool bForceExit = true; + SetExitApplicationFlag(bForceExit); + return MIstatus::failure; + } + + // Switch lldb to synchronous mode + CMICmnLLDBDebugSessionInfo &rSessionInfo( + CMICmnLLDBDebugSessionInfo::Instance()); + const bool bAsyncSetting = rSessionInfo.GetDebugger().GetAsync(); + rSessionInfo.GetDebugger().SetAsync(vbAsyncMode); + + // Execute commands from file + bool bOk = MIstatus::success; + CMIUtilString strCommand; + while (!m_bExitApp && std::getline(ifsStartScript, strCommand)) { + // Print command + bOk = CMICmnStreamStdout::TextToStdout(strCommand); + + // Skip if it's a comment or empty line + if (strCommand.empty() || strCommand[0] == '#') + continue; + + // Execute if no error + if (bOk) { + CMIUtilThreadLock lock(rSessionInfo.GetSessionMutex()); + bOk = InterpretCommand(strCommand); + } + + // Draw the prompt after command will be executed (if enabled) + bOk = bOk && CMICmnStreamStdout::WritePrompt(); + + // Exit if there is an error + if (!bOk) { + const bool bForceExit = true; + SetExitApplicationFlag(bForceExit); + break; + } + + // Wait while the handler thread handles incoming events + CMICmnLLDBDebugger::Instance().WaitForHandleEvent(); + } + + // Switch lldb back to initial mode + rSessionInfo.GetDebugger().SetAsync(bAsyncSetting); + + return bOk; +} + +//++ +// Details: Gets called when lldb-mi gets a signal. Stops the process if it was +// SIGINT. +// +// Type: Method. +// Args: signal that was delivered +// Return: None. +// Throws: None. +//-- +void CMIDriver::DeliverSignal(int signal) { + if (signal == SIGINT && + (m_eCurrentDriverState == eDriverState_RunningDebugging)) + InterpretCommand("-exec-interrupt"); +} diff --git a/src/MIDriver.h b/src/MIDriver.h new file mode 100644 index 00000000000..03bbb3e7515 --- /dev/null +++ b/src/MIDriver.h @@ -0,0 +1,180 @@ +//===-- MIDriver.h ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// Third party headers +#include + +// In-house headers: +#include "MICmdData.h" +#include "MICmnBase.h" +#include "MICmnConfig.h" +#include "MICmnStreamStdin.h" +#include "MIDriverBase.h" +#include "MIDriverMgr.h" +#include "MIUtilSingletonBase.h" + +// Declarations: +class CMICmnLLDBDebugger; +class CMICmnStreamStdout; + +//++ +//============================================================================ +// Details: MI driver implementation class. A singleton class derived from +// LLDB SBBroadcaster class. Register the instance of *this class with +// the CMIDriverMgr. The CMIDriverMgr sets the driver(s) of to start +// work depending on the one selected to work. A driver can if not able +// to handle an instruction or 'command' can pass that command onto +// another driver object registered with the Driver Manager. +//-- +class CMIDriver : public CMICmnBase, + public CMIDriverMgr::IDriver, + public CMIDriverBase, + public MI::ISingleton { + friend class MI::ISingleton; + + // Enumerations: +public: + //++ ---------------------------------------------------------------------- + // Details: The MI Driver has a running state which is used to help determine + // which specific action(s) it should take or not allow. + // The driver when operational and not shutting down alternates + // between eDriverState_RunningNotDebugging and + // eDriverState_RunningDebugging. eDriverState_RunningNotDebugging + // is normally set when a breakpoint is hit or halted. + // eDriverState_RunningDebugging is normally set when "exec-continue" + // or "exec-run" is issued. + //-- + enum DriverState_e { + eDriverState_NotRunning = 0, // The MI Driver is not operating + eDriverState_Initialising, // The MI Driver is setting itself up + eDriverState_RunningNotDebugging, // The MI Driver is operational acting on + // any MI commands sent to it + eDriverState_RunningDebugging, // The MI Driver is currently overseeing an + // inferior program that is running + eDriverState_ShuttingDown, // The MI Driver is tearing down resources and + // about exit + eDriverState_count // Always last + }; + + // Methods: +public: + // MI system + bool Initialize() override; + bool Shutdown() override; + + // MI state + bool GetExitApplicationFlag() const; + DriverState_e GetCurrentDriverState() const; + bool SetDriverStateRunningNotDebugging(); + bool SetDriverStateRunningDebugging(); + void SetDriverDebuggingArgExecutable(); + bool IsDriverDebuggingArgExecutable() const; + + // MI information about itself + const CMIUtilString &GetAppNameShort() const; + const CMIUtilString &GetAppNameLong() const; + const CMIUtilString &GetVersionDescription() const; + + // MI do work + bool WriteMessageToLog(const CMIUtilString &vMessage); + bool SetEnableFallThru(const bool vbYes); + bool GetEnableFallThru() const; + bool HaveExecutableFileNamePathOnCmdLine() const; + const CMIUtilString &GetExecutableFileNamePathOnCmdLine() const; + + // Overridden: +public: + // From CMIDriverMgr::IDriver + bool DoInitialize() override; + bool DoShutdown() override; + bool DoMainLoop() override; + lldb::SBError DoParseArgs(const int argc, const char *argv[], FILE *vpStdOut, + bool &vwbExiting) override; + CMIUtilString GetError() const override; + const CMIUtilString &GetName() const override; + lldb::SBDebugger &GetTheDebugger() override; + bool GetDriverIsGDBMICompatibleDriver() const override; + bool SetId(const CMIUtilString &vId) override; + const CMIUtilString &GetId() const override; + // From CMIDriverBase + void SetExitApplicationFlag(const bool vbForceExit) override; + bool DoFallThruToAnotherDriver(const CMIUtilString &vCmd, + CMIUtilString &vwErrMsg) override; + bool SetDriverToFallThruTo(const CMIDriverBase &vrOtherDriver) override; + FILE *GetStdin() const override; + FILE *GetStdout() const override; + FILE *GetStderr() const override; + const CMIUtilString &GetDriverName() const override; + const CMIUtilString &GetDriverId() const override; + void DeliverSignal(int signal) override; + + // Typedefs: +private: + typedef std::queue QueueStdinLine_t; + + // Methods: +private: + /* ctor */ CMIDriver(); + /* ctor */ CMIDriver(const CMIDriver &); + void operator=(const CMIDriver &); + + lldb::SBError ParseArgs(const int argc, const char *argv[], FILE *vpStdOut, + bool &vwbExiting); + bool DoAppQuit(); + bool InterpretCommand(const CMIUtilString &vTextLine); + bool InterpretCommandThisDriver(const CMIUtilString &vTextLine, + bool &vwbCmdYesValid); + CMIUtilString + WrapCLICommandIntoMICommand(const CMIUtilString &vTextLine) const; + bool InterpretCommandFallThruDriver(const CMIUtilString &vTextLine, + bool &vwbCmdYesValid); + bool ExecuteCommand(const SMICmdData &vCmdData); + bool StartWorkerThreads(); + bool StopWorkerThreads(); + bool InitClientIDEToMIDriver() const; + bool InitClientIDEEclipse() const; + bool LocalDebugSessionStartupExecuteCommands(); + bool ExecuteCommandFile(const bool vbAsyncMode); + + // Overridden: +private: + // From CMICmnBase + /* dtor */ ~CMIDriver() override; + + // Attributes: +private: + static const CMIUtilString ms_constAppNameShort; + static const CMIUtilString ms_constAppNameLong; + static const CMIUtilString ms_constMIVersion; + // + bool m_bFallThruToOtherDriverEnabled; // True = yes fall through, false = do + // not pass on command + CMIUtilThreadMutex m_threadMutex; + bool m_bDriverIsExiting; // True = yes, driver told to quit, false = continue + // working + void *m_handleMainThread; // *this driver is run by the main thread + CMICmnStreamStdin &m_rStdin; + CMICmnLLDBDebugger &m_rLldbDebugger; + CMICmnStreamStdout &m_rStdOut; + DriverState_e m_eCurrentDriverState; + bool m_bHaveExecutableFileNamePathOnCmdLine; // True = yes, executable given + // as one of the parameters to + // the MI Driver, false = not + // found + CMIUtilString m_strCmdLineArgExecuteableFileNamePath; + bool m_bDriverDebuggingArgExecutable; // True = the MI Driver (MI mode) is + // debugging executable passed as + // argument, + // false = running via a client (e.g. Eclipse) + bool m_bHaveCommandFileNamePathOnCmdLine; // True = file with initial commands + // given as one of the parameters to + // the MI Driver, false = not found + CMIUtilString m_strCmdLineArgCommandFileNamePath; +}; diff --git a/src/MIDriverBase.cpp b/src/MIDriverBase.cpp new file mode 100644 index 00000000000..b8844e28843 --- /dev/null +++ b/src/MIDriverBase.cpp @@ -0,0 +1,179 @@ +//===-- MIDriverBase.cpp ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Third party headers: +#include "lldb/API/SBBroadcaster.h" +#include "lldb/API/SBEvent.h" + +// In-house headers: +#include "MIDriverBase.h" + +//++ +// Details: CMIDriverBase constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIDriverBase::CMIDriverBase() + : m_pDriverFallThru(nullptr), m_pDriverParent(nullptr), m_bExitApp(false) {} + +//++ +// Details: CMIDriverBase destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIDriverBase::~CMIDriverBase() { m_pDriverFallThru = nullptr; } + +//++ +// Details: This function allows *this driver to call on another driver to +// perform work +// should this driver not be able to handle the client data input. +// Type: Overrideable. +// Check the error message if the function returns a failure. +// Type: Overridden. +// Args: vCmd - (R) Command instruction to interpret. +// vwErrMsg - (W) Status description on command failing. +// Return: MIstatus::success - Command succeeded. +// MIstatus::failure - Command failed. +// Throws: None. +//-- +bool CMIDriverBase::DoFallThruToAnotherDriver(const CMIUtilString &vCmd, + CMIUtilString &vwErrMsg) { + // Do nothing - override and implement. Use m_pDriverFallThru. + return MIstatus::success; +} + +//++ +// Details: This function allows *this driver to call on another driver to +// perform work +// should this driver not be able to handle the client data input. +// Type: Overrideable. +// Args: vrOtherDriver - (R) Reference to another driver object. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriverBase::SetDriverToFallThruTo(const CMIDriverBase &vrOtherDriver) { + MIunused(vrOtherDriver); + + // Do nothing - override and implement. Set m_pDriverFallThru. + + return MIstatus::success; +} + +//++ +// Details: This function allows *this driver to call functionality on the +// parent driver +// ask for information for example. +// Type: Overrideable. +// Args: vrOtherDriver - (R) Reference to another driver object. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriverBase::SetDriverParent(const CMIDriverBase &vrOtherDriver) { + MIunused(vrOtherDriver); + + // Do nothing - override and implement. Set m_pDriverParent. + + return MIstatus::success; +} + +//++ +// Details: Retrieve the parent driver to *this driver if one assigned. If +// assigned *this +// is the pass through driver that the parent driver passes work to. +// Type: Method. +// Args: None. +// Return: CMIDriverBase * - Pointer to a driver object. +// - NULL = there is not parent to *this driver. +// Throws: None. +//-- +CMIDriverBase *CMIDriverBase::GetDriversParent() const { + return m_pDriverParent; +} + +//++ +// Details: Retrieve the pointer to the other fall through driver *this driver +// is using +// (or not using). +// Type: Method. +// Args: None. +// Return: CMIDriverBase * - Pointer to other driver. +// - NULL if no driver set. +// Throws: None. +//-- +CMIDriverBase *CMIDriverBase::GetDriverToFallThruTo() const { + return m_pDriverFallThru; +} + +//++ +// Details: *this driver provides a file stream to other drivers on which *this +// driver +// write's out to and they read as expected input. *this driver is +// passing +// through commands to the (child) pass through assigned driver. +// Type: Overrideable. +// Args: None. +// Return: FILE * - Pointer to stream. +// Throws: None. +//-- +FILE *CMIDriverBase::GetStdin() const { + // Do nothing - override and implement + return nullptr; +} + +//++ +// Details: *this driver provides a file stream to other pass through assigned +// drivers +// so they know what to write to. +// Type: Overrideable. +// Args: None. +// Return: FILE * - Pointer to stream. +// Throws: None. +//-- +FILE *CMIDriverBase::GetStdout() const { + // Do nothing - override and implement + return nullptr; +} + +//++ +// Details: *this driver provides a error file stream to other pass through +// assigned drivers +// so they know what to write to. +// Type: Overrideable. +// Args: None. +// Return: FILE * - Pointer to stream. +// Throws: None. +//-- +FILE *CMIDriverBase::GetStderr() const { + // Do nothing - override and implement + return nullptr; +} + +//++ +// Details: Set the MI Driver's exit application flag. The application checks +// this flag +// after every stdin line is read so the exit may not be instantaneous. +// If vbForceExit is false the MI Driver queries its state and +// determines if is +// should exit or continue operating depending on that running state. +// Type: Overrideable. +// Args: vbForceExit - (R) True = Do not query, set state to exit, false = +// query if can/should exit right now. +// Return: None. +// Throws: None. +//-- +void CMIDriverBase::SetExitApplicationFlag(const bool vbForceExit) { + MIunused(vbForceExit); + + // Do nothing - override and implement +} diff --git a/src/MIDriverBase.h b/src/MIDriverBase.h new file mode 100644 index 00000000000..aec306e4b40 --- /dev/null +++ b/src/MIDriverBase.h @@ -0,0 +1,67 @@ +//===-- MIDriverBase.h ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// Third party headers: +#include "lldb/API/SBBroadcaster.h" +#include "lldb/API/SBDebugger.h" + +// In-house headers: +#include "MIUtilString.h" + +// Declarations: +namespace lldb { +class SBBroadcaster; +} + +//++ +//============================================================================ +// Details: MI driver base implementation class. This class has been created so +// not have to edit the lldb::SBBroadcaster class code. Functionality +// and attributes need to be common to the LLDB Driver class and the +// MI Driver class (derived from lldb::SBBroadcaster) so they can call +// upon each other for functionality fall through and allow the +// CDriverMgr to manage either (any) driver to be operated on. +// Each driver instance (the CMIDriver, LLDB::Driver) has its own +// LLDB::SBDebugger object. +//-- +class CMIDriverBase { + // Methods: +public: + /* ctor */ CMIDriverBase(); + + CMIDriverBase *GetDriverToFallThruTo() const; + CMIDriverBase *GetDriversParent() const; + + // Overrideable: +public: + /* dtor */ virtual ~CMIDriverBase(); + + virtual bool DoFallThruToAnotherDriver(const CMIUtilString &vCmd, + CMIUtilString &vwErrMsg); + virtual bool SetDriverToFallThruTo(const CMIDriverBase &vrOtherDriver); + virtual bool SetDriverParent(const CMIDriverBase &vrOtherDriver); + virtual const CMIUtilString &GetDriverName() const = 0; + virtual const CMIUtilString &GetDriverId() const = 0; + virtual void SetExitApplicationFlag(const bool vbForceExit); + + // MI provide information for the pass through (child) assigned driver + virtual FILE *GetStdin() const; + virtual FILE *GetStdout() const; + virtual FILE *GetStderr() const; + + // Attributes: +protected: + CMIDriverBase *m_pDriverFallThru; // Child driver to use should *this driver + // not be able to handle client input + CMIDriverBase *m_pDriverParent; // The parent driver who passes work to *this + // driver to do work + CMIUtilString m_strDriverId; + bool m_bExitApp; // True = Yes, exit application, false = continue execution +}; diff --git a/src/MIDriverMain.cpp b/src/MIDriverMain.cpp new file mode 100644 index 00000000000..8bc4571a458 --- /dev/null +++ b/src/MIDriverMain.cpp @@ -0,0 +1,194 @@ +//===-- MIDriverMain.cpp ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Overview: Defines the entry point for the console application. +// The MI application (project name MI) runs in two modes: +// An LLDB native driver mode where it acts no different from the +// LLDB driver. +// The other mode is the MI when it finds on the command line +// the --interpreter option. Command line argument --help on its +// own will give +// help for the LLDB driver. If entered with --interpreter then MI +// help will +// provided. +// To implement new MI commands derive a new command class from the +// command base +// class. To enable the new command for interpretation add the new +// command class +// to the command factory. The files of relevance are: +// MICmdCommands.cpp +// MICmdBase.h / .cpp +// MICmdCmd.h / .cpp + +// Third party headers: +#include "lldb/API/SBHostOS.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/PrettyStackTrace.h" +#include "llvm/Support/Signals.h" +#include +#include +#include + +// In house headers: +#include "MICmnConfig.h" +#include "MICmnResources.h" +#include "MICmnStreamStdin.h" +#include "MIDriver.h" +#include "MIDriverMgr.h" +#include "MIUtilDebug.h" +#include "Platform.h" + +#if defined(_MSC_VER) +#pragma warning( \ + once : 4530) // Warning C4530: C++ exception handler used, but unwind + // semantics are not enabled. Specify /EHsc +#endif // _MSC_VER + +// CODETAG_IOR_SIGNALS +//++ +// Details: The SIGINT signal is sent to a process by its controlling terminal +// when a +// user wishes to interrupt the process. This is typically initiated by +// pressing +// Control-C, but on some systems, the "delete" character or "break" +// key can be +// used. +// Be aware this function may be called on another thread besides the +// main thread. +// Type: Function. +// Args: vSigno - (R) Signal number. +// Return: None. +// Throws: None. +//-- +void sigint_handler(int vSigno) { +#ifdef _WIN32 // Restore handler as it is not persistent on Windows + signal(SIGINT, sigint_handler); +#endif + static std::atomic_flag g_interrupt_sent = ATOMIC_FLAG_INIT; + CMIDriverMgr &rDriverMgr = CMIDriverMgr::Instance(); + lldb::SBDebugger *pDebugger = rDriverMgr.DriverGetTheDebugger(); + if (pDebugger != nullptr) { + if (!g_interrupt_sent.test_and_set()) { + pDebugger->DispatchInputInterrupt(); + g_interrupt_sent.clear(); + } + } + + // Send signal to driver so that it can take suitable action + rDriverMgr.DeliverSignal(vSigno); +} + +//++ +// Details: Init the MI driver system. Initialize the whole driver system which +// includes +// both the original LLDB driver and the MI driver. +// Type: Function. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool DriverSystemInit() { + bool bOk = MIstatus::success; + CMIDriver &rMIDriver = CMIDriver::Instance(); + CMIDriverMgr &rDriverMgr = CMIDriverMgr::Instance(); + bOk = rDriverMgr.Initialize(); + + // Register MIDriver first as it needs to initialize and be ready + // for the Driver to get information from MIDriver when it initializes + // (LLDB Driver is registered with the Driver Manager in MI's Initialize()) + bOk = bOk && + rDriverMgr.RegisterDriver(rMIDriver, "MIDriver"); // Will be main driver + + return bOk; +} + +//++ +// Details: Shutdown the debugger system. Release / terminate resources external +// to +// specifically the MI driver. +// Type: Function. +// Args: vbAppExitOk - (R) True = No problems, false = App exiting with +// problems (investigate!). +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool DriverSystemShutdown(const bool vbAppExitOk) { + bool bOk = MIstatus::success; + + // *** Order is important here *** + CMIDriverMgr::Instance().Shutdown(); + return bOk; +} + +//++ +// Details: MI's application start point of execution. The application runs in +// two modes. +// An LLDB native driver mode where it acts no different from the LLDB +// driver. +// The other mode is the MI when it finds on the command line +// the --interpreter option. Command line argument --help on its own +// will give +// help for the LLDB driver. If entered with --interpreter then +// application +// help will provided. +// Type: Method. +// Args: argc - (R) An integer that contains the count of arguments that +// follow in +// argv. The argc parameter is always greater than or +// equal to 1. +// argv - (R) An array of null-terminated strings representing +// command-line +// arguments entered by the user of the program. By +// convention, +// argv[0] is the command with which the program is +// invoked. +// Return: int - 0 = Normal exit, program success. +// >0 = Program success with status i.e. Control-C signal +// status +// <0 = Program failed. +// -1 = Program failed reason not specified here, see MI log +// file. +// -1000 = Program failed did not initialize successfully. +// Throws: None. +//-- +int main(int argc, char const *argv[]) { +#if MICONFIG_DEBUG_SHOW_ATTACH_DBG_DLG + CMIUtilDebug::WaitForDbgAttachInfinteLoop(); +#endif // MICONFIG_DEBUG_SHOW_ATTACH_DBG_DLG + + llvm::StringRef ToolName = argv[0]; + llvm::sys::PrintStackTraceOnErrorSignal(ToolName); + llvm::PrettyStackTraceProgram X(argc, argv); + + // *** Order is important here *** + bool bOk = DriverSystemInit(); + if (!bOk) { + DriverSystemShutdown(bOk); + return -1000; + } + + // CODETAG_IOR_SIGNALS + signal(SIGINT, sigint_handler); + + bool bExiting = false; + CMIDriverMgr &rDriverMgr = CMIDriverMgr::Instance(); + bOk = bOk && rDriverMgr.ParseArgs(argc, argv, bExiting); + if (bOk && !bExiting) + bOk = rDriverMgr.DriverParseArgs(argc, argv, stdout, bExiting); + if (bOk && !bExiting) + bOk = rDriverMgr.DriverMainLoop(); + + // Logger and other resources shutdown now + DriverSystemShutdown(bOk); + + const int appResult = bOk ? 0 : -1; + + return appResult; +} diff --git a/src/MIDriverMgr.cpp b/src/MIDriverMgr.cpp new file mode 100644 index 00000000000..26195cdb741 --- /dev/null +++ b/src/MIDriverMgr.cpp @@ -0,0 +1,727 @@ +//===-- MIDriverMgr.cpp -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Third Party Headers: +#include "lldb/API/SBError.h" + +// In-house headers: +#include "MICmnLog.h" +#include "MICmnLogMediumFile.h" +#include "MICmnResources.h" +#include "MICmnStreamStdout.h" +#include "MIDriver.h" +#include "MIDriverMgr.h" +#include "MIUtilSingletonHelper.h" + +//++ +// Details: CMIDriverMgr constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIDriverMgr::CMIDriverMgr() : m_pDriverCurrent(nullptr), m_bInMi2Mode(false) {} + +//++ +// Details: CMIDriverMgr destructor. +// Type: Overridden. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIDriverMgr::~CMIDriverMgr() { Shutdown(); } + +//++ +// Details: Initialize *this manager. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriverMgr::Initialize() { + m_clientUsageRefCnt++; + + ClrErrorDescription(); + + if (m_bInitialized) + return MIstatus::success; + + bool bOk = MIstatus::success; + CMIUtilString errMsg; + + // Note initialisation order is important here as some resources depend on + // previous + MI::ModuleInit(IDS_MI_INIT_ERR_LOG, bOk, errMsg); + MI::ModuleInit(IDS_MI_INIT_ERR_RESOURCES, bOk, errMsg); + + m_bInitialized = bOk; + + if (!bOk) { + CMIUtilString strInitError(CMIUtilString::Format( + MIRSRC(IDS_MI_INIT_ERR_DRIVERMGR), errMsg.c_str())); + SetErrorDescription(strInitError); + return MIstatus::failure; + } + + return bOk; +} + +//++ +// Details: Unbind detach or release resources used by this server in general +// common +// functionality shared between versions of any server interfaces +// implemented. +// Type: Method. +// Args: vbAppExitOk - (R) True = No problems, false = App exiting with +// problems (investigate!). +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriverMgr::Shutdown() { + // Do not want a ref counter because this function needs to be called how ever + // this + // application stops running + // if( --m_clientUsageRefCnt > 0 ) + // return MIstatus::success; + + ClrErrorDescription(); + + if (!m_bInitialized) + return MIstatus::success; + + m_bInitialized = false; + + bool bOk = MIstatus::success; + CMIUtilString errMsg; + + // Tidy up + UnregisterDriverAll(); + + // Note shutdown order is important here + MI::ModuleShutdown(IDE_MI_SHTDWN_ERR_RESOURCES, bOk, errMsg); + MI::ModuleShutdown(IDS_MI_SHTDWN_ERR_LOG, bOk, errMsg); + + if (!bOk) { + SetErrorDescriptionn(MIRSRC(IDS_MI_SHTDWN_ERR_DRIVERMGR), errMsg.c_str()); + } + + return bOk; +} +//++ +// Details: Unregister all the Driver registered with *this manager. The manager +// also +// deletes +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriverMgr::UnregisterDriverAll() { + MapDriverIdToDriver_t::const_iterator it = m_mapDriverIdToDriver.begin(); + while (it != m_mapDriverIdToDriver.end()) { + IDriver *pDriver = (*it).second; + pDriver->DoShutdown(); + + // Next + ++it; + } + + m_mapDriverIdToDriver.clear(); + m_pDriverCurrent = nullptr; + + return MIstatus::success; +} + +//++ +// Details: Register a driver with *this Driver Manager. Call +// SetUseThisDriverToDoWork() +// inform the manager which driver is the one to the work. The manager +// calls +// the driver's init function which must be successful in order to +// complete the +// registration. +// Type: Method. +// Args: vrDriver - (R) The driver to register. +// vrDriverID - (R) The driver's ID to lookup by. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriverMgr::RegisterDriver(const IDriver &vrDriver, + const CMIUtilString &vrDriverID) { + if (HaveDriverAlready(vrDriver)) + return MIstatus::success; + + IDriver *pDriver = const_cast(&vrDriver); + if (!pDriver->SetId(vrDriverID)) + return MIstatus::failure; + if (!pDriver->DoInitialize()) { + SetErrorDescriptionn(MIRSRC(IDS_DRIVERMGR_DRIVER_ERR_INIT), + pDriver->GetName().c_str(), vrDriverID.c_str(), + pDriver->GetError().c_str()); + return MIstatus::failure; + } + + MapPairDriverIdToDriver_t pr(vrDriverID, pDriver); + m_mapDriverIdToDriver.insert(pr); + + return MIstatus::success; +} + +//++ +// Details: Query the Driver Manager to see if *this manager has the driver +// already +// registered. +// Type: Method. +// Args: vrDriver - (R) The driver to query. +// Return: True - registered. +// False - not registered. +// Throws: None. +//-- +bool CMIDriverMgr::HaveDriverAlready(const IDriver &vrDriver) const { + MapDriverIdToDriver_t::const_iterator it = m_mapDriverIdToDriver.begin(); + while (it != m_mapDriverIdToDriver.end()) { + const IDriver *pDrvr = (*it).second; + if (pDrvr == &vrDriver) + return true; + + // Next + ++it; + } + + return false; +} + +//++ +// Details: Unregister a driver from the Driver Manager. Call the +// SetUseThisDriverToDoWork() +// function to define another driver to do work if the one being +// unregistered did +// the work previously. +// Type: Method. +// Args: vrDriver - (R) The driver to unregister. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriverMgr::UnregisterDriver(const IDriver &vrDriver) { + const IDriver *pDrvr = nullptr; + MapDriverIdToDriver_t::const_iterator it = m_mapDriverIdToDriver.begin(); + while (it != m_mapDriverIdToDriver.end()) { + pDrvr = (*it).second; + if (pDrvr == &vrDriver) + break; + + // Next + ++it; + } + m_mapDriverIdToDriver.erase(it); + + if (m_pDriverCurrent == pDrvr) + m_pDriverCurrent = nullptr; + + return MIstatus::success; +} + +//++ +// Details: Specify the driver to do work. The Driver Manager drives this +// driver. Any +// previous driver doing work is not called anymore (so be sure the +// previous +// driver is in a tidy state before stopping it working). +// Type: Method. +// Args: vrADriver - (R) A lldb::SBBroadcaster/IDriver derived object. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriverMgr::SetUseThisDriverToDoWork(const IDriver &vrADriver) { + m_pDriverCurrent = const_cast(&vrADriver); + + const CMIUtilString msg( + CMIUtilString::Format(MIRSRC(IDS_DRIVER_SAY_DRIVER_USING), + m_pDriverCurrent->GetName().c_str())); + m_pLog->Write(msg, CMICmnLog::eLogVerbosity_Log); + + m_bInMi2Mode = m_pDriverCurrent->GetDriverIsGDBMICompatibleDriver(); + + return MIstatus::success; +} + +//++ +// Details: Ask *this manager which driver is currently doing the work. +// Type: Method. +// Args: None. +// Return: IDriver * - Pointer to a driver, NULL if there is no current working +// driver. +// Throws: None. +//-- +CMIDriverMgr::IDriver *CMIDriverMgr::GetUseThisDriverToDoWork() const { + return m_pDriverCurrent; +} + +//++ +// Details: Call this function puts *this driver to work. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriverMgr::DriverMainLoop() { + if (m_pDriverCurrent != nullptr) { + if (!m_pDriverCurrent->DoMainLoop()) { + const CMIUtilString errMsg( + CMIUtilString::Format(MIRSRC(IDS_DRIVER_ERR_MAINLOOP), + m_pDriverCurrent->GetError().c_str())); + CMICmnStreamStdout::Instance().Write(errMsg, true); + return MIstatus::failure; + } + } else { + const CMIUtilString errMsg(MIRSRC(IDS_DRIVER_ERR_CURRENT_NOT_SET)); + CMICmnStreamStdout::Instance().Write(errMsg, true); + return MIstatus::failure; + } + + return MIstatus::success; +} + +//++ +// Details: Get the current driver to validate executable command line +// arguments. +// Type: Method. +// Args: argc - (R) An integer that contains the count of arguments +// that follow in +// argv. The argc parameter is always greater than +// or equal to 1. +// argv - (R) An array of null-terminated strings representing +// command-line +// arguments entered by the user of the program. By +// convention, +// argv[0] is the command with which the program is +// invoked. +// vpStdOut - (R) Point to a standard output stream. +// vwbExiting - (W) True = *this want to exit, false = continue to +// work. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIDriverMgr::DriverParseArgs(const int argc, const char *argv[], + FILE *vpStdOut, bool &vwbExiting) { + if (m_pDriverCurrent == nullptr) { + const CMIUtilString errMsg(MIRSRC(IDS_DRIVER_ERR_CURRENT_NOT_SET)); + CMICmnStreamStdout::Instance().Write(errMsg, true); + return MIstatus::failure; + } + + const lldb::SBError error( + m_pDriverCurrent->DoParseArgs(argc, argv, vpStdOut, vwbExiting)); + bool bOk = !error.Fail(); + if (!bOk) { + CMIUtilString errMsg; + const char *pErrorCstr = error.GetCString(); + if (pErrorCstr != nullptr) + errMsg = CMIUtilString::Format(MIRSRC(IDS_DRIVER_ERR_PARSE_ARGS), + m_pDriverCurrent->GetName().c_str(), + pErrorCstr); + else + errMsg = CMIUtilString::Format(MIRSRC(IDS_DRIVER_ERR_PARSE_ARGS_UNKNOWN), + m_pDriverCurrent->GetName().c_str()); + + bOk = CMICmnStreamStdout::Instance().Write(errMsg, true); + } + + return bOk; +} + +//++ +// Details: Retrieve the current driver's last error condition. +// Type: Method. +// Args: None. +// Return: CMIUtilString - Text description. +// Throws: None. +//-- +CMIUtilString CMIDriverMgr::DriverGetError() const { + if (m_pDriverCurrent != nullptr) + return m_pDriverCurrent->GetError(); + else { + const CMIUtilString errMsg(MIRSRC(IDS_DRIVER_ERR_CURRENT_NOT_SET)); + CMICmnStreamStdout::Instance().Write(errMsg, true); + } + + return CMIUtilString(); +} + +//++ +// Details: Retrieve the current driver's name. +// Type: Method. +// Args: None. +// Return: CMIUtilString - Driver name. +// Empty string = no current working driver specified. +// Throws: None. +//-- +CMIUtilString CMIDriverMgr::DriverGetName() const { + if (m_pDriverCurrent != nullptr) + return m_pDriverCurrent->GetName(); + else { + const CMIUtilString errMsg(MIRSRC(IDS_DRIVER_ERR_CURRENT_NOT_SET)); + CMICmnStreamStdout::Instance().Write(errMsg, true); + } + + return CMIUtilString(); +} + +//++ +// Details: Retrieve the current driver's debugger object. +// Type: Method. +// Args: None. +// Return: lldb::SBDebugger * - Ptr to driver's debugger object. +// - NULL = no current working driver specified. +// Throws: None. +//-- +lldb::SBDebugger *CMIDriverMgr::DriverGetTheDebugger() { + lldb::SBDebugger *pDebugger = nullptr; + if (m_pDriverCurrent != nullptr) + pDebugger = &m_pDriverCurrent->GetTheDebugger(); + else { + const CMIUtilString errMsg(MIRSRC(IDS_DRIVER_ERR_CURRENT_NOT_SET)); + CMICmnStreamStdout::Instance().Write(errMsg, true); + } + + return pDebugger; +} + +//++ +// Details: Check the arguments given on the command line. The main purpose of +// this +// function is to check for the presence of the --interpreter option. +// Having +// this option present tells *this manager to set the CMIDriver to do +// work. If +// not use the LLDB driver. The following are options that are only +// handled by +// the CMIDriverMgr are: +// --help or -h +// --interpreter +// --version +// --versionLong +// --log +// --executable +// --log-dir +// The above arguments are not handled by any driver object except for +// --executable. +// The options --interpreter and --executable in code act very similar. +// The +// --executable is necessary to differentiate whither the MI Driver is +// being using +// by a client i.e. Eclipse or from the command line. Eclipse issues +// the option +// --interpreter and also passes additional arguments which can be +// interpreted as an +// executable if called from the command line. Using --executable tells +// the MI +// Driver is being called the command line and that the executable +// argument is indeed +// a specified executable an so actions commands to set up the +// executable for a +// debug session. Using --interpreter on the command line does not +// action additional +// commands to initialise a debug session and so be able to launch the +// process. The directory +// where the log file is created is specified using --log-dir. +// Type: Method. +// Args: argc - (R) An integer that contains the count of arguments +// that follow in +// argv. The argc parameter is always greater than +// or equal to 1. +// argv - (R) An array of null-terminated strings representing +// command-line +// arguments entered by the user of the program. By +// convention, +// argv[0] is the command with which the program is +// invoked. +// vwbExiting - (W) True = *this want to exit, Reasons: help, +// invalid arg(s), +// version information only. +// False = Continue to work, start debugger i.e. +// Command +// interpreter. +// Return: lldb::SBError - LLDB current error status. +// Throws: None. +//-- +bool CMIDriverMgr::ParseArgs(const int argc, const char *argv[], + bool &vwbExiting) { + bool bOk = MIstatus::success; + + vwbExiting = false; + + // Print MI application path to the Log file + const CMIUtilString appPath( + CMIUtilString::Format(MIRSRC(IDS_MI_APP_FILEPATHNAME), argv[0])); + bOk = m_pLog->Write(appPath, CMICmnLog::eLogVerbosity_Log); + + // Print application arguments to the Log file + const bool bHaveArgs(argc >= 2); + CMIUtilString strArgs(MIRSRC(IDS_MI_APP_ARGS)); + if (!bHaveArgs) { + strArgs += MIRSRC(IDS_WORD_NONE); + bOk = bOk && m_pLog->Write(strArgs, CMICmnLog::eLogVerbosity_Log); + } else { + for (MIint i = 1; i < argc; i++) { + strArgs += CMIUtilString::Format("%d:'%s' ", i, + CMIUtilString::WithNullAsEmpty(argv[i])); + } + bOk = bOk && m_pLog->Write(strArgs, CMICmnLog::eLogVerbosity_Log); + } + + // Look for the command line options + bool bHaveArgInterpret = false; + bool bHaveArgVersion = false; + bool bHaveArgVersionLong = false; + bool bHaveArgLog = false; + bool bHaveArgLogDir = false; + bool bHaveArgHelp = false; + CMIUtilString strLogDir; + + bHaveArgInterpret = true; + if (bHaveArgs) { + // CODETAG_MIDRIVE_CMD_LINE_ARG_HANDLING + for (MIint i = 1; i < argc; i++) { + // *** Add args to help in GetHelpOnCmdLineArgOptions() *** + const CMIUtilString strArg(argv[i]); + + // Argument "--executable" is also check for in CMIDriver::ParseArgs() + if (("--interpreter" == strArg) || // Given by the client such as Eclipse + ("--executable" == strArg)) // Used to specify that there + // is executable argument also + // on the command line + { // See fn description. + bHaveArgInterpret = true; + } + if ("--version" == strArg) { + bHaveArgVersion = true; + } + if ("--versionLong" == strArg) { + bHaveArgVersionLong = true; + } + if ("--log" == strArg) { + bHaveArgLog = true; + } + if (0 == strArg.compare(0, 10, "--log-dir=")) { + strLogDir = strArg.substr(10, CMIUtilString::npos); + bHaveArgLogDir = true; + } + if (("--help" == strArg) || ("-h" == strArg)) { + bHaveArgHelp = true; + } + } + } + + if (bHaveArgLog) { + CMICmnLog::Instance().SetEnabled(true); + } + + if (bHaveArgLogDir) { + bOk = bOk && CMICmnLogMediumFile::Instance().SetDirectory(strLogDir); + } + + // Todo: Remove this output when MI is finished. It is temporary to persuade + // Eclipse plugin to work. + // Eclipse reads this literally and will not work unless it gets this + // exact version text. + // Handle --version option (ignore the --interpreter option if present) + if (bHaveArgVersion) { + vwbExiting = true; + bOk = bOk && + CMICmnStreamStdout::Instance().WriteMIResponse( + MIRSRC(IDE_MI_VERSION_GDB)); + return bOk; + } + + // Todo: Make this the --version when the above --version version is removed + // Handle --versionlong option (ignore the --interpreter option if present) + if (bHaveArgVersionLong) { + vwbExiting = true; + bOk = + bOk && CMICmnStreamStdout::Instance().WriteMIResponse(GetAppVersion()); + return bOk; + } + + // Both '--help' and '--interpreter' means give help for MI only. Without + // '--interpreter' help the LLDB driver is working and so help is for that. + if (bHaveArgHelp && bHaveArgInterpret) { + vwbExiting = true; + bOk = bOk && + CMICmnStreamStdout::Instance().WriteMIResponse( + GetHelpOnCmdLineArgOptions()); + return bOk; + } + + // This makes the assumption that there is at least one MI compatible + // driver registered and one LLDB driver registered and the CMIDriver + // is the first one found. + // ToDo: Implement a better solution that handle any order, any number + // of drivers. Or this 'feature' may be removed if deemed not required. + IDriver *pLldbDriver = GetFirstNonMIDriver(); + IDriver *pMi2Driver = GetFirstMIDriver(); + if (bHaveArgInterpret && (pMi2Driver != nullptr)) + bOk = bOk && SetUseThisDriverToDoWork(*pMi2Driver); + else if (pLldbDriver != nullptr) + bOk = bOk && SetUseThisDriverToDoWork(*pLldbDriver); + else { + if (bOk) { + vwbExiting = true; + const CMIUtilString msg(MIRSRC(IDS_DRIVER_ERR_NON_REGISTERED)); + bOk = bOk && CMICmnStreamStdout::Instance().WriteMIResponse(msg); + } + } + + return bOk; +} + +//++ +// Details: Return formatted application version and name information. +// Type: Method. +// Args: None. +// Return: CMIUtilString - Text data. +// Throws: None. +//-- +CMIUtilString CMIDriverMgr::GetAppVersion() const { + const CMIUtilString strProj(MIRSRC(IDS_PROJNAME)); + const CMIUtilString strVsn(CMIDriver::Instance().GetVersionDescription()); + const CMIUtilString strGdb(MIRSRC(IDE_MI_VERSION_GDB)); + const CMIUtilString strVrsnInfo(CMIUtilString::Format( + "%s\n%s\n%s", strProj.c_str(), strVsn.c_str(), strGdb.c_str())); + + return strVrsnInfo; +} + +//++ +// Details: Return formatted help information on all the MI command line +// options. +// Type: Method. +// Args: None. +// Return: CMIUtilString - Text data. +// Throws: None. +//-- +CMIUtilString CMIDriverMgr::GetHelpOnCmdLineArgOptions() const { + const CMIUtilString pHelp[] = { + MIRSRC(IDE_MI_APP_DESCRIPTION), MIRSRC(IDE_MI_APP_INFORMATION), + MIRSRC(IDE_MI_APP_ARG_USAGE), MIRSRC(IDE_MI_APP_ARG_HELP), + MIRSRC(IDE_MI_APP_ARG_VERSION), MIRSRC(IDE_MI_APP_ARG_VERSION_LONG), + MIRSRC(IDE_MI_APP_ARG_INTERPRETER), MIRSRC(IDE_MI_APP_ARG_SOURCE), + MIRSRC(IDE_MI_APP_ARG_EXECUTEABLE), + MIRSRC(IDE_MI_APP_ARG_SYNCHRONOUS), + CMIUtilString::Format( + MIRSRC(IDE_MI_APP_ARG_APP_LOG), + CMICmnLogMediumFile::Instance().GetFileName().c_str()), + MIRSRC(IDE_MI_APP_ARG_APP_LOG_DIR), MIRSRC(IDE_MI_APP_ARG_EXECUTABLE), + MIRSRC(IDS_CMD_QUIT_HELP), MIRSRC(IDE_MI_APP_ARG_EXAMPLE)}; + const MIuint nHelpItems = sizeof pHelp / sizeof pHelp[0]; + CMIUtilString strHelp; + for (MIuint i = 0; i < nHelpItems; i++) { + strHelp += pHelp[i]; + strHelp += "\n\n"; + } + + return strHelp; +} + +//++ +// Details: Search the registered drivers and return the first driver which says +// it is +// GDB/MI compatible i.e. the CMIDriver class. +// Type: Method. +// Args: None. +// Return: IDriver * - Ptr to driver, NULL = no driver found. +// Throws: None. +//-- +CMIDriverMgr::IDriver *CMIDriverMgr::GetFirstMIDriver() const { + IDriver *pDriver = nullptr; + MapDriverIdToDriver_t::const_iterator it = m_mapDriverIdToDriver.begin(); + while (it != m_mapDriverIdToDriver.end()) { + const CMIUtilString &rDrvId = (*it).first; + MIunused(rDrvId); + IDriver *pDvr = (*it).second; + if (pDvr->GetDriverIsGDBMICompatibleDriver()) { + pDriver = pDvr; + break; + } + + // Next + ++it; + } + + return pDriver; +} + +//++ +// Details: Search the registered drivers and return the first driver which says +// it is +// not GDB/MI compatible i.e. the LLDB Driver class. +// Type: Method. +// Args: None. +// Return: IDriver * - Ptr to driver, NULL = no driver found. +// Throws: None. +//-- +CMIDriverMgr::IDriver *CMIDriverMgr::GetFirstNonMIDriver() const { + IDriver *pDriver = nullptr; + MapDriverIdToDriver_t::const_iterator it = m_mapDriverIdToDriver.begin(); + while (it != m_mapDriverIdToDriver.end()) { + const CMIUtilString &rDrvId = (*it).first; + MIunused(rDrvId); + IDriver *pDvr = (*it).second; + if (!pDvr->GetDriverIsGDBMICompatibleDriver()) { + pDriver = pDvr; + break; + } + + // Next + ++it; + } + + return pDriver; +} + +//++ +// Details: Search the registered drivers and return driver with the specified +// ID. +// Type: Method. +// Args: vrDriverId - (R) ID of a driver. +// Return: IDriver * - Ptr to driver, NULL = no driver found. +// Throws: None. +//-- +CMIDriverMgr::IDriver * +CMIDriverMgr::GetDriver(const CMIUtilString &vrDriverId) const { + MapDriverIdToDriver_t::const_iterator it = + m_mapDriverIdToDriver.find(vrDriverId); + if (it == m_mapDriverIdToDriver.end()) + return nullptr; + + IDriver *pDriver = (*it).second; + + return pDriver; +} + +//++ +// Details: Gets called when lldb-mi gets a signal. Passed signal to current +// driver. +// +// Type: Method. +// Args: signal that was delivered +// Return: None. +// Throws: None. +//-- +void CMIDriverMgr::DeliverSignal(int signal) { + if (m_pDriverCurrent != nullptr) + m_pDriverCurrent->DeliverSignal(signal); +} diff --git a/src/MIDriverMgr.h b/src/MIDriverMgr.h new file mode 100644 index 00000000000..a7a1dbbb9bf --- /dev/null +++ b/src/MIDriverMgr.h @@ -0,0 +1,128 @@ +//===-- MIDriverMgr.h -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// Third party headers: +#include "lldb/API/SBDebugger.h" +#include + +// In-house headers: +#include "MICmnBase.h" +#include "MICmnLog.h" +#include "MIUtilSingletonBase.h" +#include "MIUtilString.h" + +//++ +//============================================================================ +// Details: MI Driver Manager. Register lldb::SBBroadcaster derived Driver type +// objects with *this manager. The manager does not own driver objects +// registered with it and so will not delete when this manager is +// shutdown. The Driver flagged as "use this one" will be set as +// current +// driver and will be the one that is used. Other drivers are not +// operated. A Driver can call another Driver should it not handle a +// command. +// It also initializes other resources as part it's setup such as the +// Logger and Resources objects (explicit indicate *this object +// requires +// those objects (modules/components) to support it's own +// functionality). +// The Driver manager is the first object instantiated as part of the +// MI code base. It is also the first thing to interpret the command +// line arguments passed to the executable. Bases on options it +// understands the manage will set up the appropriate driver or give +// help information. Other options are passed on to the driver chosen +// to do work. +// Each driver instance (the CMIDriver, LLDB::Driver) has its own +// LLDB::SBDebugger. +// Singleton class. +//-- +class CMIDriverMgr : public CMICmnBase, public MI::ISingleton { + friend MI::ISingleton; + + // Class: +public: + //++ + // Description: Driver deriver objects need this interface to work with + // *this manager. + //-- + class IDriver { + public: + virtual bool DoInitialize() = 0; + virtual bool DoShutdown() = 0; + virtual bool DoMainLoop() = 0; + virtual lldb::SBError DoParseArgs(const int argc, const char *argv[], + FILE *vpStdOut, bool &vwbExiting) = 0; + virtual CMIUtilString GetError() const = 0; + virtual const CMIUtilString &GetName() const = 0; + virtual lldb::SBDebugger &GetTheDebugger() = 0; + virtual bool GetDriverIsGDBMICompatibleDriver() const = 0; + virtual bool SetId(const CMIUtilString &vId) = 0; + virtual const CMIUtilString &GetId() const = 0; + virtual void DeliverSignal(int signal) = 0; + + // Not part of the interface, ignore + /* dtor */ virtual ~IDriver() {} + }; + + // Methods: +public: + // MI system + bool Initialize() override; + bool Shutdown() override; + // + CMIUtilString GetAppVersion() const; + bool RegisterDriver(const IDriver &vrADriver, + const CMIUtilString &vrDriverID); + bool UnregisterDriver(const IDriver &vrADriver); + bool SetUseThisDriverToDoWork( + const IDriver &vrADriver); // Specify working main driver + IDriver *GetUseThisDriverToDoWork() const; + bool ParseArgs(const int argc, const char *argv[], bool &vwbExiting); + IDriver *GetDriver(const CMIUtilString &vrDriverId) const; + // + // MI Proxy fn to current specified working driver + bool DriverMainLoop(); + bool DriverParseArgs(const int argc, const char *argv[], FILE *vpStdOut, + bool &vwbExiting); + CMIUtilString DriverGetError() const; + CMIUtilString DriverGetName() const; + lldb::SBDebugger *DriverGetTheDebugger(); + void DeliverSignal(int signal); + + // Typedef: +private: + typedef std::map MapDriverIdToDriver_t; + typedef std::pair MapPairDriverIdToDriver_t; + + // Methods: +private: + /* ctor */ CMIDriverMgr(); + /* ctor */ CMIDriverMgr(const CMIDriverMgr &); + void operator=(const CMIDriverMgr &); + // + bool HaveDriverAlready(const IDriver &vrMedium) const; + bool UnregisterDriverAll(); + IDriver *GetFirstMIDriver() const; + IDriver *GetFirstNonMIDriver() const; + CMIUtilString GetHelpOnCmdLineArgOptions() const; + + // Overridden: +private: + // From CMICmnBase + /* dtor */ ~CMIDriverMgr() override; + + // Attributes: +private: + MapDriverIdToDriver_t m_mapDriverIdToDriver; + IDriver *m_pDriverCurrent; // This driver is used by this manager to do work. + // It is the main driver. + bool m_bInMi2Mode; // True = --interpreter entered on the cmd line, false = + // operate LLDB driver (non GDB) +}; diff --git a/src/MIExtensions.txt b/src/MIExtensions.txt new file mode 100644 index 00000000000..70508b1d37b --- /dev/null +++ b/src/MIExtensions.txt @@ -0,0 +1,104 @@ +# -file-exec-and-symbols now takes two new (optional) options: + +Synopsis + + -file-exec-and-symbols [-p ] [-r ] + +Specify the executable file to be debugged. This file is the one from which the symbol table is also read. +When debugging remote targets specify a remote-file for execution and a file from which symbols are read. +The optional platform is the name of the platform, e.g., "remote-ios" or "ios-simulator". The remote-file +is the on-device path to the exe. + +# -data-info-line + +Synopsis + + -data-info-line *
+ -data-info-line : + +Provides information about a source line. The input can be
like 0x12345678 or : +where file is a name of source file and line is the line number. As a result the command returns the following +fields: + start - address of the first instruction which refers to that source line + end - address of the last instruction which refers to that source line + file - the file name + line - the line number +The last two fields are useful in case you have specified a source line using its address. + +Example: + -data-info-line *0x100000f80 + ^done,start="0x0000000100000f80",end="0x0000000100000f94",file="/Users/IliaK/p/hello.cpp",line="15" + + -data-info-line hello.cpp:15 + ^done,start="0x0000000100000f80",end="0x0000000100000f94",file="/Users/IliaK/p/hello.cpp",line="15" + +# -data-read-memory-bytes + +Synopsis + + -data-read-memory-bytes [--thread ] [--frame ] [-o ]
+ +Where: + + `address` + An expression specifying the start of the memory range to read. + `count` + Number of bytes to read. + `byte-offset` + Relative offset in bytes from `address` where reading should start. + `thread-id` + Integer identifier of the thread within which the expression should be evaluated, + if this option is omitted the currently selected thread will be used. + This option is not in the MI specification but is implemented by GDB. + `frame-index` + Index of the frame within which the expression should be evaluated, + if this option is omitted the currently selected frame will be used. + This option is not in the MI specification but is implemented by GDB. + +Reads a block of memory from the specified range. + +Note that currently this command works in an all-or-nothing fashion where it either reads the entire +block of memory successfully and returns it as a single block, or it returns an error. This doesn't +quite match up with the MI specification that says that subsets of the specified range may be +returned as individual blocks if only some of the memory within the specified range is accessible. + +The result record for this command may contain one or more tuples representing the blocks of memory +that were read, where each tuple has the following fields: + + `begin` + The start of the address range for this block (in hex notation). + `end` + The end of the address range for this block (in hex notation). + `offset` + Offset of this block from `address` (that was passed in as an argument). + `contents` + The actual data in this block (in hex notation). + +Example: + + (gdb) + -data-read-memory-bytes &array 4 + ^done,memory=[{begin="0x00007fffffffeccc",offset="0x0000000000000000",end="0x00007fffffffecd0",contents="01020304"}] + (gdb) + +# =library-loaded notification + +The =library-loaded notification has 4 extra fields: + symbols-loaded - indicates that there are symbols for the loaded library + symbols-path - if symbols are exist then it contains a path for symbols of the loaded library + loaded_addr - contains an address of the loaded library or "-" if address isn't resolved yet + size - contains the size in bytes of the section loaded at 'loaded_addr' + +For example: + =library-loaded,id="/Users/IliaK/p/hello",target-name="/Users/IliaK/p/hello",host-name="/Users/IliaK/p/hello",symbols-loaded="1",symbols-path="/Users/IliaK/p/hello.dSYM/Contents/Resources/DWARF/hello",loaded_addr="-",size="4096" + =library-loaded,id="/usr/lib/dyld",target-name="/usr/lib/dyld",host-name="/usr/lib/dyld",symbols-loaded="0",loaded_addr="0x00007fff5fc00000",size="4096" + +# -target-attach + +Synopsis + +Additional syntax provided by lldb-mi: + -target-attach -n [--waitfor] + +Attach to an executable. Using -n allows specifying an executable name to attach to. +Using this with --watifor can do a deffered attach. The flags -n and --waitfor match the syntax of lldb proper's 'process attach' command. diff --git a/src/MIReadMe.txt b/src/MIReadMe.txt new file mode 100644 index 00000000000..51316ed879c --- /dev/null +++ b/src/MIReadMe.txt @@ -0,0 +1,37 @@ +======================================================================== + The MI Driver - LLDB Machine Interface V2 (MI) Project Overview +======================================================================== + +The MI Driver is a stand alone executable that either be used via a +client i.e. Eclipse or directly from the command line. + +For help information on using the MI driver type at the command line: + + lldb-mi --interpreter --help + +A blog about the MI Driver is available on CodePlay's website. Although it may not be +completely accurate after the recent changes in lldb-mi. +http://www.codeplay.com/portal/lldb-mi-driver---part-1-introduction + +In MI mode and invoked with --log option, lldb-mi generates lldb-mi-log.txt +This file keeps a history of the MI Driver's activity for one session. It is +used to aid the debugging of the MI Driver. It also gives warnings about +command's which do not support certain argument or options. + +Note any command or text sent to the MI Driver in MI mode that is not a command +registered in the MI Driver's Command Factory will be rejected and an error message +will be generated. + +All the files prefix with MI are specifically for the MI driver code only. +File MIDriverMain.cpp contains the executables main() function. + +========================================================================= +Current limitations: +1. Not all commands and their options have been implemented. Please see +the source code for details. +2. LLDB-MI may have additional arguments not used in GDB MI. Please see +MIExtensions.txt + +========================================================================= +The MI Driver build configuration: +MICmnConfig.h defines various preprocessor build options. diff --git a/src/MIUtilDateTimeStd.cpp b/src/MIUtilDateTimeStd.cpp new file mode 100644 index 00000000000..e92250a4f2d --- /dev/null +++ b/src/MIUtilDateTimeStd.cpp @@ -0,0 +1,84 @@ +//===-- MIUtilDateTimeStd.cpp -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MIUtilDateTimeStd.h" +#include "MICmnResources.h" + +//++ +// Details: CMIUtilDateTimeStd constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIUtilDateTimeStd::CMIUtilDateTimeStd() {} + +//++ +// Details: CMIUtilDateTimeStd destructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIUtilDateTimeStd::~CMIUtilDateTimeStd() {} + +//++ +// Details: Retrieve system local current date. Format is MM/DD/YYYY. +// Type: Method. +// Args: None. +// Return: CMIUtilString - Text description. +// Throws: None. +//-- +CMIUtilString CMIUtilDateTimeStd::GetDate() { + CMIUtilString strDate(MIRSRC(IDS_WORD_INVALIDBRKTS)); + + std::time(&m_rawTime); + const std::tm *pTi = std::localtime(&m_rawTime); + if (std::strftime(&m_pScratch[0], sizeof(m_pScratch), "%d/%m/%y", pTi) > 0) + strDate = m_pScratch; + + return strDate; +} + +//++ +// Details: Retrieve system local current time. Format is HH:MM:SS 24 hour +// clock. +// Type: Method. +// Args: None. +// Return: CMIUtilString - Text description. +// Throws: None. +//-- +CMIUtilString CMIUtilDateTimeStd::GetTime() { + std::time(&m_rawTime); + const std::tm *pTi = std::localtime(&m_rawTime); + const CMIUtilString seconds(CMIUtilString::Format("%d", pTi->tm_sec)); + const CMIUtilString zero((seconds.length() == 1) ? "0" : ""); + const CMIUtilString strTime(CMIUtilString::Format( + "%d:%d:%s%s", pTi->tm_hour, pTi->tm_min, zero.c_str(), seconds.c_str())); + + return strTime; +} + +//++ +// Details: Retrieve system local current date and time in yyyy-MM-dd--HH-mm-ss +// format for log file names. +// Type: Method. +// Args: None. +// Return: CMIUtilString - Text description. +// Throws: None. +//-- +CMIUtilString CMIUtilDateTimeStd::GetDateTimeLogFilename() { + std::time(&m_rawTime); + const std::tm *pTi = std::localtime(&m_rawTime); + const CMIUtilString strTime(CMIUtilString::Format( + "%d%02d%02d%02d%02d%02d", pTi->tm_year + 1900, pTi->tm_mon, pTi->tm_mday, + pTi->tm_hour, pTi->tm_min, pTi->tm_sec)); + + return strTime; +} diff --git a/src/MIUtilDateTimeStd.h b/src/MIUtilDateTimeStd.h new file mode 100644 index 00000000000..1a8e30c5654 --- /dev/null +++ b/src/MIUtilDateTimeStd.h @@ -0,0 +1,40 @@ +//===-- MIUtilDateTimeStd.h -------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// Third party headers +#include + +// In-house headers: +#include "MIUtilString.h" + +//++ +//============================================================================ +// Details: MI common code utility class. Used to retrieve system local date +// time. +//-- +class CMIUtilDateTimeStd { + // Methods: +public: + /* ctor */ CMIUtilDateTimeStd(); + + CMIUtilString GetDate(); + CMIUtilString GetTime(); + CMIUtilString GetDateTimeLogFilename(); + + // Overrideable: +public: + // From CMICmnBase + /* dtor */ virtual ~CMIUtilDateTimeStd(); + + // Attributes: +private: + std::time_t m_rawTime; + char m_pScratch[16]; +}; diff --git a/src/MIUtilDebug.cpp b/src/MIUtilDebug.cpp new file mode 100644 index 00000000000..4b418f390ca --- /dev/null +++ b/src/MIUtilDebug.cpp @@ -0,0 +1,91 @@ +//===-- MIUtilDebug.cpp -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Third party headers: +#ifdef _WIN32 +#include +#endif + +// In-house headers: +#include "MICmnLog.h" +#include "MIDriver.h" +#include "MIUtilDebug.h" + +//++ +// Details: CMIUtilDebug constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIUtilDebug::CMIUtilDebug() {} + +//++ +// Details: CMIUtilDebug destructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIUtilDebug::~CMIUtilDebug() {} + +//++ +// Details: Temporarily stall the process/application to give the programmer the +// opportunity to attach a debugger. How to use: Put a break in the +// programmer +// where you want to visit, run the application then attach your +// debugger to the +// application. Hit the debugger's pause button and the debugger should +// should +// show this loop. Change the i variable value to break out of the loop +// and +// visit your break point. +// Type: Static method. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMIUtilDebug::WaitForDbgAttachInfinteLoop() { + MIuint i = 0; + while (i == 0) { + const std::chrono::milliseconds time(100); + std::this_thread::sleep_for(time); + } +} + + +// Instantiations: +CMICmnLog &CMIUtilDebugFnTrace::ms_rLog = CMICmnLog::Instance(); +MIuint CMIUtilDebugFnTrace::ms_fnDepthCnt = 0; + +//++ +// Details: CMIUtilDebugFnTrace constructor. +// Type: Method. +// Args: vFnName - (R) The text to insert into the log. +// Return: None. +// Throws: None. +//-- +CMIUtilDebugFnTrace::CMIUtilDebugFnTrace(const CMIUtilString &vFnName) + : m_strFnName(vFnName) { + const CMIUtilString txt( + CMIUtilString::Format("%d>%s", ++ms_fnDepthCnt, m_strFnName.c_str())); + ms_rLog.Write(txt, CMICmnLog::eLogVerbosity_FnTrace); +} + +//++ +// Details: CMIUtilDebugFnTrace destructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIUtilDebugFnTrace::~CMIUtilDebugFnTrace() { + const CMIUtilString txt( + CMIUtilString::Format("%d<%s", ms_fnDepthCnt--, m_strFnName.c_str())); + ms_rLog.Write(txt, CMICmnLog::eLogVerbosity_FnTrace); +} diff --git a/src/MIUtilDebug.h b/src/MIUtilDebug.h new file mode 100644 index 00000000000..537907ae304 --- /dev/null +++ b/src/MIUtilDebug.h @@ -0,0 +1,79 @@ +//===-- MIUtilDebug.h -------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +#define MI_USE_DEBUG_TRACE_FN // Undefine to compile out fn trace code + +// In-house headers: +#include "MIUtilString.h" + +// Declarations: +class CMICmnLog; + +//++ +//============================================================================ +// Details: MI debugging aid utility class. +//-- +class CMIUtilDebug { + // Statics: +public: + static void WaitForDbgAttachInfinteLoop(); + + // Methods: +public: + /* ctor */ CMIUtilDebug(); + + // Overrideable: +public: + // From CMICmnBase + /* dtor */ virtual ~CMIUtilDebug(); +}; + +//++ +//============================================================================ +// Details: MI debug utility class. Used to indicate the current function +// depth in the call stack. It uses the CMIlCmnLog logger to output +// the current fn trace information. +// Use macro MI_TRACEFN( "Some fn name" ) and implement the scope of +// the functions you wish to build up a trace off. +// Use preprocessor definition MI_USE_DEBUG_TRACE_FN to turn off or on +// tracing code. +//-- +class CMIUtilDebugFnTrace { + // Methods: +public: + /* ctor */ CMIUtilDebugFnTrace(const CMIUtilString &vFnName); + + // Overrideable: +public: + // From CMICmnBase + /* dtor */ virtual ~CMIUtilDebugFnTrace(); + + // Attributes: +private: + const CMIUtilString m_strFnName; + + static CMICmnLog &ms_rLog; + static MIuint ms_fnDepthCnt; // Increment count as fn depth increases, + // decrement count as fn stack pops off +}; + +//++ +//============================================================================ +// Details: Take the given text and send it to the server's Logger to output to +// the +// trace file. +// Type: Compile preprocess. +// Args: x - (R) Message (may be seen by user). +//-- +#ifdef MI_USE_DEBUG_TRACE_FN +#define MI_TRACEFN(x) CMIUtilDebugFnTrace __MITrace(x) +#else +#define MI_TRACEFN(x) +#endif // MI_USE_DEBUG_TRACE_FN diff --git a/src/MIUtilFileStd.cpp b/src/MIUtilFileStd.cpp new file mode 100644 index 00000000000..df1249a02a1 --- /dev/null +++ b/src/MIUtilFileStd.cpp @@ -0,0 +1,288 @@ +//===-- MIUtilFileStd.cpp ---------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// 2021.3.15 Add support for OHOS target. +// Copyright (c) 2021 Huawei Device Co., Ltd. All rights reserved. +// +//===----------------------------------------------------------------------===// + +// Third party headers +#include +#include +#include +#include + +#if defined(_WIN32) +#include +#endif + +// In-house headers: +#include "MICmnResources.h" +#include "MIUtilFileStd.h" +#include "lldb/Host/FileSystem.h" + +#include "llvm/Support/ConvertUTF.h" +#include "llvm/Support/Errno.h" + +//++ +// Details: CMIUtilFileStd constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIUtilFileStd::CMIUtilFileStd() + : m_fileNamePath(CMIUtilString()), m_pFileHandle(nullptr) +#if defined(_WIN32) + , + m_constCharNewLine("\r\n") +#else + , + m_constCharNewLine("\n") +#endif // #if defined( _WIN32 ) + , + m_bFileError(false) { +} + +//++ +// Details: CMIUtilFileStd destructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIUtilFileStd::~CMIUtilFileStd() { Close(); } + +//++ +// Details: Open file for writing. On the first call to this function after +// *this object +// is created the file is either created or replace, from then on open +// only opens +// an existing file. +// Type: Method. +// Args: vFileNamePath - (R) File name path. +// vwrbNewCreated - (W) True - file recreated, false - file appended +// too. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIUtilFileStd::CreateWrite(const CMIUtilString &vFileNamePath, + bool &vwrbNewCreated) { + // Reset + m_bFileError = false; + vwrbNewCreated = false; + + if (vFileNamePath.empty()) { + m_bFileError = true; + SetErrorDescription(MIRSRC(IDS_UTIL_FILE_ERR_INVALID_PATHNAME)); + return MIstatus::failure; + } + + // File is already open so exit + if (m_pFileHandle != nullptr) + return MIstatus::success; + +#if !defined(_WIN32) + // Open with 'write' and 'binary' mode + m_pFileHandle = llvm::sys::RetryAfterSignal(nullptr, ::fopen, + vFileNamePath.c_str(), "wb"); +#else + // Open a file with exclusive write and shared read permissions + std::wstring path; + if (llvm::ConvertUTF8toWide(vFileNamePath.c_str(), path)) + m_pFileHandle = ::_wfsopen(path.c_str(), L"wb", _SH_DENYWR); + else { + errno = EINVAL; + m_pFileHandle = nullptr; + } +#endif // !defined( _WIN32 ) + + if (m_pFileHandle == nullptr) { + m_bFileError = true; + SetErrorDescriptionn(MIRSRC(IDS_UTIL_FILE_ERR_OPENING_FILE), + strerror(errno), vFileNamePath.c_str()); + return MIstatus::failure; + } + + vwrbNewCreated = true; + m_fileNamePath = vFileNamePath; + + return MIstatus::success; +} + +//++ +// Details: Write data to existing opened file. +// Type: Method. +// Args: vData - (R) Text data. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIUtilFileStd::Write(const CMIUtilString &vData) { + if (vData.size() == 0) + return MIstatus::success; + + if (m_bFileError) + return MIstatus::failure; + + if (m_pFileHandle == nullptr) { + m_bFileError = true; + SetErrorDescriptionn(MIRSRC(IDE_UTIL_FILE_ERR_WRITING_NOTOPEN), + m_fileNamePath.c_str()); + return MIstatus::failure; + } + + // Get the string size + MIuint size = vData.size(); + if (::fwrite(vData.c_str(), 1, size, m_pFileHandle) == size) { + // Flush the data to the file + ::fflush(m_pFileHandle); + return MIstatus::success; + } + + // Not all of the data has been transferred + m_bFileError = true; + SetErrorDescriptionn(MIRSRC(IDE_UTIL_FILE_ERR_WRITING_FILE), + m_fileNamePath.c_str()); + return MIstatus::failure; +} + +//++ +// Details: Write data to existing opened file. +// Type: Method. +// Args: vData - (R) Text data. +// vCharCnt - (R) Text data length. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIUtilFileStd::Write(const char *vpData, const MIuint vCharCnt) { + if (vCharCnt == 0) + return MIstatus::success; + + if (m_bFileError) + return MIstatus::failure; + + if (m_pFileHandle == nullptr) { + m_bFileError = true; + SetErrorDescriptionn(MIRSRC(IDE_UTIL_FILE_ERR_WRITING_NOTOPEN), + m_fileNamePath.c_str()); + return MIstatus::failure; + } + + if (::fwrite(vpData, 1, vCharCnt, m_pFileHandle) == vCharCnt) { + // Flush the data to the file + ::fflush(m_pFileHandle); + return MIstatus::success; + } + + // Not all of the data has been transferred + m_bFileError = true; + SetErrorDescriptionn(MIRSRC(IDE_UTIL_FILE_ERR_WRITING_FILE), + m_fileNamePath.c_str()); + return MIstatus::failure; +} + +//++ +// Details: Close existing opened file. Note Close() must must an open! +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMIUtilFileStd::Close() { + if (m_pFileHandle == nullptr) + return; + + ::fclose(m_pFileHandle); + m_pFileHandle = nullptr; + // m_bFileError = false; Do not reset as want to remain until next attempt at + // open or create +} + +//++ +// Details: Retrieve state of whether the file is ok. +// Type: Method. +// Args: None. +// Return: True - file ok. +// False - file has a problem. +// Throws: None. +//-- +bool CMIUtilFileStd::IsOk() const { return !m_bFileError; } + +//++ +// Details: Status on a file existing already. +// Type: Method. +// Args: vFileNamePath. +// Return: True - Exists. +// False - Not found. +// Throws: None. +//-- +bool CMIUtilFileStd::IsFileExist(const CMIUtilString &vFileNamePath) const { + if (vFileNamePath.empty()) + return false; + + FILE *pTmp = nullptr; + pTmp = llvm::sys::RetryAfterSignal(nullptr, ::fopen, + vFileNamePath.c_str(), "wb"); + if (pTmp != nullptr) { + ::fclose(pTmp); + return true; + } + + return false; +} + +//++ +// Details: Retrieve the file current carriage line return characters used. +// Type: Method. +// Args: None. +// Return: CMIUtilString & - Text. +// Throws: None. +//-- +const CMIUtilString &CMIUtilFileStd::GetLineReturn() const { + return m_constCharNewLine; +} + +//++ +// Details: Given a file name directory path, strip off the filename and return +// the path. +// It look for either backslash or forward slash. +// Type: Method. +// Args: vDirectoryPath - (R) Text directory path. +// Return: CMIUtilString - Directory path. +// Throws: None. +//-- +CMIUtilString +CMIUtilFileStd::StripOffFileName(const CMIUtilString &vDirectoryPath) { + const size_t nPos = vDirectoryPath.rfind('\\'); + size_t nPos2 = vDirectoryPath.rfind('/'); + if ((nPos == std::string::npos) && (nPos2 == std::string::npos)) + return vDirectoryPath; + + if (nPos > nPos2) + nPos2 = nPos; + + const CMIUtilString strPath(vDirectoryPath.substr(0, nPos2).c_str()); + return strPath; +} + +//++ +// Details: Return either backslash or forward slash appropriate to the OS this +// application +// is running on. +// Type: Static method. +// Args: None. +// Return: char - '/' or '\' character. +// Throws: None. +//-- +char CMIUtilFileStd::GetSlash() { +#if !defined(_WIN32) + return '/'; +#else + return '\\'; +#endif // !defined( _WIN32 ) +} diff --git a/src/MIUtilFileStd.h b/src/MIUtilFileStd.h new file mode 100644 index 00000000000..e2e590b6aaf --- /dev/null +++ b/src/MIUtilFileStd.h @@ -0,0 +1,48 @@ +//===-- MIUtilFileStd.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MICmnBase.h" +#include "MIUtilString.h" + +//++ +//============================================================================ +// Details: MI common code utility class. File handling. +//-- +class CMIUtilFileStd : public CMICmnBase { + // Static: +public: + static char GetSlash(); + + // Methods: +public: + /* ctor */ CMIUtilFileStd(); + // + bool CreateWrite(const CMIUtilString &vFileNamePath, bool &vwrbNewCreated); + bool Write(const CMIUtilString &vData); + bool Write(const char *vpData, const MIuint vCharCnt); + void Close(); + bool IsOk() const; + bool IsFileExist(const CMIUtilString &vFileNamePath) const; + const CMIUtilString &GetLineReturn() const; + static CMIUtilString StripOffFileName(const CMIUtilString &vDirectoryPath); + + // Overridden: +public: + // From CMICmnBase + /* dtor */ ~CMIUtilFileStd() override; + + // Attributes: +private: + CMIUtilString m_fileNamePath; + FILE *m_pFileHandle; + CMIUtilString m_constCharNewLine; + bool m_bFileError; // True = have a file error ATM, false = all ok +}; diff --git a/src/MIUtilMapIdToVariant.cpp b/src/MIUtilMapIdToVariant.cpp new file mode 100644 index 00000000000..9361cedab3e --- /dev/null +++ b/src/MIUtilMapIdToVariant.cpp @@ -0,0 +1,99 @@ +//===-- MIUtilMapIdToVariant.cpp --------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MIUtilMapIdToVariant.h" + +//++ +// Details: CMIUtilMapIdToVariant constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIUtilMapIdToVariant::CMIUtilMapIdToVariant() {} + +//++ +// Details: CMIUtilMapIdToVariant destructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIUtilMapIdToVariant::~CMIUtilMapIdToVariant() {} + +//++ +// Details: Remove at the data from *this container. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMIUtilMapIdToVariant::Clear() { m_mapKeyToVariantValue.clear(); } + +//++ +// Details: Check an ID is present already in *this container. +// Type: Method. +// Args: vId - (R) Unique ID i.e. GUID. +// Return: True - registered. +// False - not found. +// Throws: None. +//-- +bool CMIUtilMapIdToVariant::HaveAlready(const CMIUtilString &vId) const { + const MapKeyToVariantValue_t::const_iterator it = + m_mapKeyToVariantValue.find(vId); + return it != m_mapKeyToVariantValue.end(); +} + +//++ +// Details: Determine if *this container is currently holding any data. +// Type: Method. +// Args: None. +// Return: bool - True - Yes empty, false - one or more data object present. +// Throws: None. +//-- +bool CMIUtilMapIdToVariant::IsEmpty() const { + return m_mapKeyToVariantValue.empty(); +} + +//++ +// Details: Check the ID is valid to be registered. +// Type: Method. +// Args: vId - (R) Unique ID i.e. GUID. +// Return: True - valid. +// False - not valid. +// Throws: None. +//-- +bool CMIUtilMapIdToVariant::IsValid(const CMIUtilString &vId) const { + bool bValid = true; + + if (vId.empty()) + bValid = false; + + return bValid; +} + +//++ +// Details: Remove from *this contain a data object specified by ID. The data +// object +// when removed also calls its destructor should it have one. +// Type: Method. +// Args: vId - (R) Unique ID i.e. GUID. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIUtilMapIdToVariant::Remove(const CMIUtilString &vId) { + const MapKeyToVariantValue_t::const_iterator it = + m_mapKeyToVariantValue.find(vId); + if (it != m_mapKeyToVariantValue.end()) { + m_mapKeyToVariantValue.erase(it); + } + + return MIstatus::success; +} diff --git a/src/MIUtilMapIdToVariant.h b/src/MIUtilMapIdToVariant.h new file mode 100644 index 00000000000..ba3e176e0c4 --- /dev/null +++ b/src/MIUtilMapIdToVariant.h @@ -0,0 +1,129 @@ +//===-- MIUtilMapIdToVariant.h ----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// Third party headers: +#include + +// In-house headers: +#include "MICmnBase.h" +#include "MICmnResources.h" +#include "MIUtilString.h" +#include "MIUtilVariant.h" + +//++ +//============================================================================ +// Details: MI common code utility class. Map type container that hold general +// object types (by being a variant wrapper) +// objects by ID. +//-- +class CMIUtilMapIdToVariant : public CMICmnBase { + // Methods: +public: + /* ctor */ CMIUtilMapIdToVariant(); + + template bool Add(const CMIUtilString &vId, const T &vData); + void Clear(); + template + bool Get(const CMIUtilString &vId, T &vrwData, bool &vrwbFound) const; + bool HaveAlready(const CMIUtilString &vId) const; + bool IsEmpty() const; + bool Remove(const CMIUtilString &vId); + + // Overridden: +public: + // From CMICmnBase + /* dtor */ ~CMIUtilMapIdToVariant() override; + + // Typedefs: +private: + typedef std::map MapKeyToVariantValue_t; + typedef std::pair MapPairKeyToVariantValue_t; + + // Methods: +private: + bool IsValid(const CMIUtilString &vId) const; + + // Attributes: + MapKeyToVariantValue_t m_mapKeyToVariantValue; +}; + +//++ +// Details: Add to *this container a data object of general type identified by +// an ID. +// If the data with that ID already exists in the container it is +// replace with +// the new data specified. +// Type: Method. +// Args: T - The data object's variable type. +// vId - (R) Unique ID i.e. GUID. +// vData - (R) The general data object to be stored of some type. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +template +bool CMIUtilMapIdToVariant::Add(const CMIUtilString &vId, const T &vData) { + if (!IsValid(vId)) { + SetErrorDescription(CMIUtilString::Format( + MIRSRC(IDS_VARIANT_ERR_MAP_KEY_INVALID), vId.c_str())); + return MIstatus::failure; + } + + const bool bOk = HaveAlready(vId) ? Remove(vId) : MIstatus::success; + if (bOk) { + CMIUtilVariant data; + data.Set(vData); + MapPairKeyToVariantValue_t pr(vId, data); + m_mapKeyToVariantValue.insert(pr); + } + + return bOk; +} + +//++ +// Details: Retrieve a data object from *this container identified by the +// specified ID. +// Type: Method. +// Args: T - The data object's variable type. +// vId - (R) Unique ID i.e. GUID. +// vrwData - (W) Copy of the data object held. +// vrwbFound - (W) True = data found, false = data not found. +// Return: MIstatus::success - Function succeeded. +// MIstatus::failure - Function failed. +// Throws: None. +//-- +template +bool CMIUtilMapIdToVariant::Get(const CMIUtilString &vId, T &vrwData, + bool &vrwbFound) const { + vrwbFound = false; + + if (!IsValid(vId)) { + SetErrorDescription(CMIUtilString::Format( + MIRSRC(IDS_VARIANT_ERR_MAP_KEY_INVALID), vId.c_str())); + return MIstatus::failure; + } + + const MapKeyToVariantValue_t::const_iterator it = + m_mapKeyToVariantValue.find(vId); + if (it != m_mapKeyToVariantValue.end()) { + const CMIUtilVariant &rData = (*it).second; + const T *pDataObj = rData.Get(); + if (pDataObj != nullptr) { + vrwbFound = true; + vrwData = *pDataObj; + return MIstatus::success; + } else { + SetErrorDescription(MIRSRC(IDS_VARIANT_ERR_USED_BASECLASS)); + return MIstatus::failure; + } + } + + return MIstatus::success; +} diff --git a/src/MIUtilSingletonBase.h b/src/MIUtilSingletonBase.h new file mode 100644 index 00000000000..e76fff72b27 --- /dev/null +++ b/src/MIUtilSingletonBase.h @@ -0,0 +1,52 @@ +//===-- MIUtilSingletonBase.h -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +namespace MI { + +// MI::ISingleton base class usage: +// +// class CMIDerivedClass +// : public MI::ISingleton< CMIDerivedClass > +// { +// friend MI::ISingleton< CMIDerivedClass >; +// +// // Overridden: +// public: +// // From MI::ISingleton +// bool Initialize() override; +// bool Shutdown() override; +// }; + +//++ +//============================================================================ +// Details: Base class for the singleton pattern. +// Gotchas: Derived class must specify MI::ISingleton<> as a friend class. +//-- +template class ISingleton { + // Statics: +public: + // Return an instance of the derived class + static T &Instance() { + // This will fail if the derived class has not + // declared itself to be a friend of MI::ISingleton + static T instance; + + return instance; + } + + // Overrideable: +public: + virtual bool Initialize() = 0; + virtual bool Shutdown() = 0; + // + /* dtor */ virtual ~ISingleton() {} +}; + +} // namespace MI diff --git a/src/MIUtilSingletonHelper.h b/src/MIUtilSingletonHelper.h new file mode 100644 index 00000000000..b9ac61a58e7 --- /dev/null +++ b/src/MIUtilSingletonHelper.h @@ -0,0 +1,81 @@ +//===-- MIUtilSingletonHelper.h ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In house headers: +#include "MICmnResources.h" +#include "MIUtilString.h" + +namespace MI { + +//++ +//============================================================================ +// Details: Short cut helper function to simplify repeated initialisation of +// MI components (singletons) required by a client module. +// Type: Template method. +// Args: vErrorResrcId - (R) The string resource ID error message +// identifier to place in errMsg. +// vwrbOk - (RW) On input True = Try to initialize MI driver +// module. +// On output True = MI driver module initialise +// successfully. +// vwrErrMsg - (W) MI driver module initialise error description +// on failure. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +//-- +template +bool ModuleInit(const MIint vErrorResrcId, bool &vwrbOk, + CMIUtilString &vwrErrMsg) { + if (vwrbOk && !T::Instance().Initialize()) { + vwrbOk = MIstatus::failure; + vwrErrMsg = CMIUtilString::Format( + MIRSRC(vErrorResrcId), T::Instance().GetErrorDescription().c_str()); + } + + return vwrbOk; +} + +//++ +//============================================================================ +// Details: Short cut helper function to simplify repeated shutdown of +// MI components (singletons) required by a client module. +// Type: Template method. +// Args: vErrorResrcId - (R) The string resource ID error message +// identifier +// to place in errMsg. +// vwrbOk - (W) If not already false make false on module +// shutdown failure. +// vwrErrMsg - (RW) Append to existing error description string +// MI +// driver module initialise error description on +// failure. +// Return: True - Module shutdown succeeded. +// False - Module shutdown failed. +//-- +template +bool ModuleShutdown(const MIint vErrorResrcId, bool &vwrbOk, + CMIUtilString &vwrErrMsg) { + bool bOk = MIstatus::success; + + if (!T::Instance().Shutdown()) { + const bool bMoreThanOneError(!vwrErrMsg.empty()); + bOk = MIstatus::failure; + if (bMoreThanOneError) + vwrErrMsg += ", "; + vwrErrMsg += CMIUtilString::Format( + MIRSRC(vErrorResrcId), T::Instance().GetErrorDescription().c_str()); + } + + vwrbOk = bOk ? vwrbOk : MIstatus::failure; + + return bOk; +} + +} // namespace MI diff --git a/src/MIUtilString.cpp b/src/MIUtilString.cpp new file mode 100644 index 00000000000..986de963450 --- /dev/null +++ b/src/MIUtilString.cpp @@ -0,0 +1,915 @@ +//===-- MIUtilString.cpp ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Third party headers +#include "llvm/Support/Compiler.h" +#include +#include +#include +#include +#include +#include +#include + +// In-house headers: +#include "MIUtilString.h" + +//++ +// Details: CMIUtilString constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIUtilString::CMIUtilString() : std::string() {} + +//++ +// Details: CMIUtilString constructor. +// Type: Method. +// Args: vpData - Pointer to UTF8 text data. +// Return: None. +// Throws: None. +//-- +CMIUtilString::CMIUtilString(const char *vpData) + : std::string(WithNullAsEmpty(vpData)) {} + +//++ +// Details: CMIUtilString constructor. +// Type: Method. +// Args: vpStr - Text data. +// Return: None. +// Throws: None. +//-- +CMIUtilString::CMIUtilString(const std::string &vrStr) : std::string(vrStr) {} + +//++ +// Details: CMIUtilString assignment operator. +// Type: Method. +// Args: vpRhs - Pointer to UTF8 text data. +// Return: CMIUtilString & - *this string. +// Throws: None. +//-- +CMIUtilString &CMIUtilString::operator=(const char *vpRhs) { + assign(WithNullAsEmpty(vpRhs)); + return *this; +} + +//++ +// Details: CMIUtilString assignment operator. +// Type: Method. +// Args: vrRhs - The other string to copy from. +// Return: CMIUtilString & - *this string. +// Throws: None. +//-- +CMIUtilString &CMIUtilString::operator=(const std::string &vrRhs) { + assign(vrRhs); + return *this; +} + +//++ +// Details: CMIUtilString destructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIUtilString::~CMIUtilString() {} + +//++ +// Details: Perform a snprintf format style on a string data. A new string +// object is +// created and returned. +// Type: Static method. +// Args: vrFormat - (R) Format string data instruction. +// vArgs - (R) Var list args of any type. +// Return: CMIUtilString - Number of splits found in the string data. +// Throws: None. +//-- +CMIUtilString CMIUtilString::FormatPriv(const CMIUtilString &vrFormat, + va_list vArgs) { + CMIUtilString strResult; + MIint nFinal = 0; + MIint n = vrFormat.size(); + + // IOR: mysterious crash in this function on some windows builds not able to + // duplicate but found article which may be related. Crash occurs in + // vsnprintf() or va_copy(). + // Duplicate vArgs va_list argument pointer to ensure that it can be safely + // used in a new frame. + // http://julipedia.meroh.net/2011/09/using-vacopy-to-safely-pass-ap.html + va_list argsDup; + va_copy(argsDup, vArgs); + + // Create a copy va_list to reset when we spin + va_list argsCpy; + va_copy(argsCpy, argsDup); + + if (n == 0) + return strResult; + + n = n << 4; // Reserve 16 times as much the length of the vrFormat + + std::unique_ptr pFormatted; + while (1) { + pFormatted.reset(new char[n + 1]); // +1 for safety margin + ::strncpy(&pFormatted[0], vrFormat.c_str(), n); + + // We need to restore the variable argument list pointer to the start again + // before running vsnprintf() more then once + va_copy(argsDup, argsCpy); + + nFinal = ::vsnprintf(&pFormatted[0], n, vrFormat.c_str(), argsDup); + if ((nFinal < 0) || (nFinal >= n)) + n += abs(nFinal - n + 1); + else + break; + } + + va_end(argsCpy); + va_end(argsDup); + + strResult = pFormatted.get(); + + return strResult; +} + +//++ +// Details: Perform a snprintf format style on a string data. A new string +// object is +// created and returned. +// Type: Static method. +// Args: vFormat - (R) Format string data instruction. +// ... - (R) Var list args of any type. +// Return: CMIUtilString - Number of splits found in the string data. +// Throws: None. +//-- +CMIUtilString CMIUtilString::Format(const char *vFormating, ...) { + va_list args; + va_start(args, vFormating); + CMIUtilString strResult = + CMIUtilString::FormatPriv(WithNullAsEmpty(vFormating), args); + va_end(args); + + return strResult; +} + +//++ +// Details: Perform a snprintf format style on a string data. A new string +// object is +// created and returned. +// Type: Static method. +// Args: vrFormat - (R) Format string data instruction. +// vArgs - (R) Var list args of any type. +// Return: CMIUtilString - Number of splits found in the string data. +// Throws: None. +//-- +CMIUtilString CMIUtilString::FormatValist(const CMIUtilString &vrFormating, + va_list vArgs) { + return CMIUtilString::FormatPriv(vrFormating, vArgs); +} + +//++ +// Details: Splits string into array of strings using delimiter. If multiple +// delimiter +// are found in sequence then they are not added to the list of splits. +// Type: Method. +// Args: vData - (R) String data to be split up. +// vDelimiter - (R) Delimiter char or text. +// vwVecSplits - (W) Container of splits found in string data. +// Return: size_t - Number of splits found in the string data. +// Throws: None. +//-- +size_t CMIUtilString::Split(const CMIUtilString &vDelimiter, + VecString_t &vwVecSplits) const { + vwVecSplits.clear(); + + if (this->empty() || vDelimiter.empty()) + return 0; + + const size_t nLen(length()); + size_t nOffset(0); + do { + // Find first occurrence which doesn't match to the delimiter + const size_t nSectionPos(FindFirstNot(vDelimiter, nOffset)); + if (nSectionPos == std::string::npos) + break; + + // Find next occurrence of the delimiter after section + size_t nNextDelimiterPos(FindFirst(vDelimiter, nSectionPos)); + if (nNextDelimiterPos == std::string::npos) + nNextDelimiterPos = nLen; + + // Extract string between delimiters + const size_t nSectionLen(nNextDelimiterPos - nSectionPos); + const std::string strSection(substr(nSectionPos, nSectionLen)); + vwVecSplits.push_back(strSection); + + // Next + nOffset = nNextDelimiterPos + 1; + } while (nOffset < nLen); + + return vwVecSplits.size(); +} + +//++ +// Details: Splits string into array of strings using delimiter. However the +// string is +// also considered for text surrounded by quotes. Text with quotes +// including the +// delimiter is treated as a whole. If multiple delimiter are found in +// sequence +// then they are not added to the list of splits. Quotes that are +// embedded in +// the string as string formatted quotes are ignored (proceeded by a +// '\\') i.e. +// "\"MI GDB local C++.cpp\":88". +// Type: Method. +// Args: vData - (R) String data to be split up. +// vDelimiter - (R) Delimiter char or text. +// vwVecSplits - (W) Container of splits found in string data. +// Return: size_t - Number of splits found in the string data. +// Throws: None. +//-- +size_t CMIUtilString::SplitConsiderQuotes(const CMIUtilString &vDelimiter, + VecString_t &vwVecSplits) const { + vwVecSplits.clear(); + + if (this->empty() || vDelimiter.empty()) + return 0; + + const size_t nLen(length()); + size_t nOffset(0); + do { + // Find first occurrence which doesn't match to the delimiter + const size_t nSectionPos(FindFirstNot(vDelimiter, nOffset)); + if (nSectionPos == std::string::npos) + break; + + // Find next occurrence of the delimiter after (quoted) section + const bool bSkipQuotedText(true); + bool bUnmatchedQuote(false); + size_t nNextDelimiterPos( + FindFirst(vDelimiter, bSkipQuotedText, bUnmatchedQuote, nSectionPos)); + if (bUnmatchedQuote) { + vwVecSplits.clear(); + return 0; + } + if (nNextDelimiterPos == std::string::npos) + nNextDelimiterPos = nLen; + + // Extract string between delimiters + const size_t nSectionLen(nNextDelimiterPos - nSectionPos); + const std::string strSection(substr(nSectionPos, nSectionLen)); + vwVecSplits.push_back(strSection); + + // Next + nOffset = nNextDelimiterPos + 1; + } while (nOffset < nLen); + + return vwVecSplits.size(); +} + +//++ +// Details: Split string into lines using \n and return an array of strings. +// Type: Method. +// Args: vwVecSplits - (W) Container of splits found in string data. +// Return: size_t - Number of splits found in the string data. +// Throws: None. +//-- +size_t CMIUtilString::SplitLines(VecString_t &vwVecSplits) const { + return Split("\n", vwVecSplits); +} + +//++ +// Details: Remove '\n' from the end of string if found. It does not alter +// *this string. +// Type: Method. +// Args: None. +// Return: CMIUtilString - New version of the string. +// Throws: None. +//-- +CMIUtilString CMIUtilString::StripCREndOfLine() const { + const size_t nPos = rfind('\n'); + if (nPos == std::string::npos) + return *this; + + const CMIUtilString strNew(substr(0, nPos)); + + return strNew; +} + +//++ +// Details: Remove all '\n' from the string and replace with a space. It does +// not alter +// *this string. +// Type: Method. +// Args: None. +// Return: CMIUtilString - New version of the string. +// Throws: None. +//-- +CMIUtilString CMIUtilString::StripCRAll() const { + return FindAndReplace("\n", " "); +} + +//++ +// Details: Find and replace all matches of a sub string with another string. It +// does not +// alter *this string. +// Type: Method. +// Args: vFind - (R) The string to look for. +// vReplaceWith - (R) The string to replace the vFind match. +// Return: CMIUtilString - New version of the string. +// Throws: None. +//-- +CMIUtilString +CMIUtilString::FindAndReplace(const CMIUtilString &vFind, + const CMIUtilString &vReplaceWith) const { + if (vFind.empty() || this->empty()) + return *this; + + size_t nPos = find(vFind); + if (nPos == std::string::npos) + return *this; + + CMIUtilString strNew(*this); + while (nPos != std::string::npos) { + strNew.replace(nPos, vFind.length(), vReplaceWith); + nPos += vReplaceWith.length(); + nPos = strNew.find(vFind, nPos); + } + + return strNew; +} + +//++ +// Details: Check if *this string is a decimal number. +// Type: Method. +// Args: None. +// Return: bool - True = yes number, false not a number. +// Throws: None. +//-- +bool CMIUtilString::IsNumber() const { + if (empty()) + return false; + + if ((at(0) == '-') && (length() == 1)) + return false; + + const size_t nPos = find_first_not_of("-.0123456789"); + return nPos == std::string::npos; +} + +//++ +// Details: Check if *this string is a hexadecimal number. +// Type: Method. +// Args: None. +// Return: bool - True = yes number, false not a number. +// Throws: None. +//-- +bool CMIUtilString::IsHexadecimalNumber() const { + // Compare '0x..' prefix + if ((strncmp(c_str(), "0x", 2) != 0) && (strncmp(c_str(), "0X", 2) != 0)) + return false; + + // Skip '0x..' prefix + const size_t nPos = find_first_not_of("01234567890ABCDEFabcedf", 2); + return nPos == std::string::npos; +} + +//++ +// Details: Extract the number from the string. The number can be either a +// hexadecimal or +// natural number. It cannot contain other non-numeric characters. +// Type: Method. +// Args: vwrNumber - (W) Number extracted from the string. +// Return: bool - True = yes number, false not a number. +// Throws: None. +//-- +bool CMIUtilString::ExtractNumber(MIint64 &vwrNumber) const { + vwrNumber = 0; + + if (!IsNumber()) { + return ExtractNumberFromHexadecimal(vwrNumber); + } + + std::stringstream ss(const_cast(*this)); + ss >> vwrNumber; + + return true; +} + +//++ +// Details: Extract the number from the hexadecimal string.. +// Type: Method. +// Args: vwrNumber - (W) Number extracted from the string. +// Return: bool - True = yes number, false not a number. +// Throws: None. +//-- +bool CMIUtilString::ExtractNumberFromHexadecimal(MIint64 &vwrNumber) const { + vwrNumber = 0; + + const size_t nPos = find_first_not_of("xX01234567890ABCDEFabcedf"); + if (nPos != std::string::npos) + return false; + + errno = 0; + const MIuint64 nNum = ::strtoull(this->c_str(), nullptr, 16); + if (errno == ERANGE) + return false; + + vwrNumber = static_cast(nNum); + + return true; +} + +//++ +// Details: Determine if the text is all valid alpha numeric characters. Letters +// can be +// either upper or lower case. +// Type: Static method. +// Args: vpText - (R) The text data to examine. +// Return: bool - True = yes all alpha, false = one or more chars is non alpha. +// Throws: None. +//-- +bool CMIUtilString::IsAllValidAlphaAndNumeric(const char *vpText) { + const size_t len = ::strlen(WithNullAsEmpty(vpText)); + if (len == 0) + return false; + + for (size_t i = 0; i < len; i++, vpText++) { + const char c = *vpText; + if (::isalnum((int)c) == 0) + return false; + } + + return true; +} + +//++ +// Details: Check if two strings share equal contents. +// Type: Method. +// Args: vrLhs - (R) String A. +// vrRhs - (R) String B. +// Return: bool - True = yes equal, false - different. +// Throws: None. +//-- +bool CMIUtilString::Compare(const CMIUtilString &vrLhs, + const CMIUtilString &vrRhs) { + // Check the sizes match + if (vrLhs.size() != vrRhs.size()) + return false; + + return (::strncmp(vrLhs.c_str(), vrRhs.c_str(), vrLhs.size()) == 0); +} + +//++ +// Details: Remove from either end of *this string the following: " \t\n\v\f\r". +// Type: Method. +// Args: None. +// Return: CMIUtilString - Trimmed string. +// Throws: None. +//-- +CMIUtilString CMIUtilString::Trim() const { + CMIUtilString strNew(*this); + const char *pWhiteSpace = " \t\n\v\f\r"; + const size_t nPos = find_last_not_of(pWhiteSpace); + if (nPos != std::string::npos) { + strNew = substr(0, nPos + 1); + } + const size_t nPos2 = strNew.find_first_not_of(pWhiteSpace); + if (nPos2 != std::string::npos) { + strNew = strNew.substr(nPos2); + } + + return strNew; +} + +//++ +// Details: Remove from either end of *this string the specified character. +// Type: Method. +// Args: None. +// Return: CMIUtilString - Trimmed string. +// Throws: None. +//-- +CMIUtilString CMIUtilString::Trim(const char vChar) const { + CMIUtilString strNew(*this); + const size_t nLen = strNew.length(); + if (nLen > 1) { + if ((strNew[0] == vChar) && (strNew[nLen - 1] == vChar)) + strNew = strNew.substr(1, nLen - 2); + } + + return strNew; +} + +//++ +// Details: Do a printf equivalent for printing a number in binary i.e. "b%llB". +// Type: Static method. +// Args: vnDecimal - (R) The number to represent in binary. +// Return: CMIUtilString - Binary number in text. +// Throws: None. +//-- +CMIUtilString CMIUtilString::FormatBinary(const MIuint64 vnDecimal) { + CMIUtilString strBinaryNumber; + + const MIuint nConstBits = 64; + MIuint nRem[nConstBits + 1]; + MIint i = 0; + MIuint nLen = 0; + MIuint64 nNum = vnDecimal; + while ((nNum > 0) && (nLen < nConstBits)) { + nRem[i++] = nNum % 2; + nNum = nNum >> 1; + nLen++; + } + char pN[nConstBits + 1]; + MIuint j = 0; + for (i = nLen; i > 0; --i, j++) { + pN[j] = '0' + nRem[i - 1]; + } + pN[j] = 0; // String NUL termination + + strBinaryNumber = CMIUtilString::Format("0b%s", &pN[0]); + + return strBinaryNumber; +} + +//++ +// Details: Remove from a string doubled up characters so only one set left. +// Characters +// are only removed if the previous character is already a same +// character. +// Type: Method. +// Args: vChar - (R) The character to search for and remove adjacent +// duplicates. +// Return: CMIUtilString - New version of the string. +// Throws: None. +//-- +CMIUtilString CMIUtilString::RemoveRepeatedCharacters(const char vChar) { + return RemoveRepeatedCharacters(0, vChar); +} + +//++ +// Details: Recursively remove from a string doubled up characters so only one +// set left. +// Characters are only removed if the previous character is already a +// same +// character. +// Type: Method. +// Args: vChar - (R) The character to search for and remove adjacent +// duplicates. +// vnPos - Character position in the string. +// Return: CMIUtilString - New version of the string. +// Throws: None. +//-- +CMIUtilString CMIUtilString::RemoveRepeatedCharacters(size_t vnPos, + const char vChar) { + const char cQuote = '"'; + + // Look for first quote of two + const size_t nPos = find(cQuote, vnPos); + if (nPos == std::string::npos) + return *this; + + const size_t nPosNext = nPos + 1; + if (nPosNext > length()) + return *this; + + if (at(nPosNext) == cQuote) { + *this = substr(0, nPos) + substr(nPosNext, length()); + RemoveRepeatedCharacters(nPosNext, vChar); + } + + return *this; +} + +//++ +// Details: Is the text in *this string surrounded by quotes. +// Type: Method. +// Args: None. +// Return: bool - True = Yes string is quoted, false = no quoted. +// Throws: None. +//-- +bool CMIUtilString::IsQuoted() const { + const char cQuote = '"'; + + if (at(0) != cQuote) + return false; + + const size_t nLen = length(); + return !((nLen > 0) && (at(nLen - 1) != cQuote)); +} + +//++ +// Details: Find first occurrence in *this string which matches the pattern. +// Type: Method. +// Args: vrPattern - (R) The pattern to search for. +// vnPos - The starting position at which to start searching. +// (Dflt = 0) +// Return: size_t - The position of the first substring that match. +// Throws: None. +//-- +size_t CMIUtilString::FindFirst(const CMIUtilString &vrPattern, + size_t vnPos /* = 0 */) const { + return find(vrPattern, vnPos); +} + +//++ +// Details: Find first occurrence in *this string which matches the pattern and +// isn't surrounded by quotes. +// Type: Method. +// Args: vrPattern - (R) The pattern to search for. +// vbSkipQuotedText - (R) True = don't look at quoted text, +// false = otherwise. +// vrwbNotFoundClosedQuote - (W) True = parsing error: unmatched +// quote, false = otherwise. +// vnPos - Position of the first character in the +// string to be considered in the search. (Dflt = 0) +// Return: size_t - The position of the first substring that matches and isn't +// quoted. +// Throws: None. +//-- +size_t CMIUtilString::FindFirst(const CMIUtilString &vrPattern, + const bool vbSkipQuotedText, + bool &vrwbNotFoundClosedQuote, + size_t vnPos /* = 0 */) const { + vrwbNotFoundClosedQuote = false; + + if (!vbSkipQuotedText) + return FindFirst(vrPattern, vnPos); + + const size_t nLen(length()); + + size_t nPos = vnPos; + do { + const size_t nQuotePos(FindFirstQuote(nPos)); + const size_t nPatternPos(FindFirst(vrPattern, nPos)); + if (nQuotePos == std::string::npos) + return nPatternPos; + + const size_t nQuoteClosedPos = FindFirstQuote(nQuotePos + 1); + if (nQuoteClosedPos == std::string::npos) { + vrwbNotFoundClosedQuote = true; + return std::string::npos; + } + + if ((nPatternPos == std::string::npos) || (nPatternPos < nQuotePos)) + return nPatternPos; + + nPos = nQuoteClosedPos + 1; + } while (nPos < nLen); + + return std::string::npos; +} + +//++ +// Details: Find first occurrence in *this string which doesn't match the +// pattern. +// Type: Method. +// Args: vrPattern - (R) The pattern to search for. +// vnPos - Position of the first character in the string to be +// considered in the search. (Dflt = 0) +// Return: size_t - The position of the first character that doesn't match. +// Throws: None. +//-- +size_t CMIUtilString::FindFirstNot(const CMIUtilString &vrPattern, + size_t vnPos /* = 0 */) const { + const size_t nLen(length()); + const size_t nPatternLen(vrPattern.length()); + + size_t nPatternPos(vnPos); + do { + const bool bMatchPattern(compare(nPatternPos, nPatternLen, vrPattern) == 0); + if (!bMatchPattern) + return nPatternPos; + nPatternPos += nPatternLen; + } while (nPatternPos < nLen); + + return std::string::npos; +} + +//++ +// Details: Find first occurrence of not escaped quotation mark in *this string. +// Type: Method. +// Args: vnPos - Position of the first character in the string to be +// considered in the search. +// Return: size_t - The position of the quotation mark. +// Throws: None. +//-- +size_t CMIUtilString::FindFirstQuote(size_t vnPos) const { + const char cBckSlash('\\'); + const char cQuote('"'); + const size_t nLen(length()); + + size_t nPos = vnPos; + do { + const size_t nBckSlashPos(find(cBckSlash, nPos)); + const size_t nQuotePos(find(cQuote, nPos)); + if ((nBckSlashPos == std::string::npos) || (nQuotePos == std::string::npos)) + return nQuotePos; + + if (nQuotePos < nBckSlashPos) + return nQuotePos; + + // Skip 2 characters: First is '\', second is that which is escaped by '\' + nPos = nBckSlashPos + 2; + } while (nPos < nLen); + + return std::string::npos; +} + +//++ +// Details: Get escaped string from *this string. +// Type: Method. +// Args: None. +// Return: CMIUtilString - The escaped version of the initial string. +// Throws: None. +//-- +CMIUtilString CMIUtilString::Escape(bool vbEscapeQuotes /* = false */) const { + const size_t nLen(length()); + CMIUtilString strNew; + strNew.reserve(nLen); + for (size_t nIndex(0); nIndex < nLen; ++nIndex) { + const char cUnescapedChar((*this)[nIndex]); + if (cUnescapedChar == '"' && vbEscapeQuotes) + strNew.append("\\\""); + else + strNew.append(ConvertToPrintableASCII((char)cUnescapedChar)); + } + return strNew; +} + +//++ +// Details: Get string with backslashes in front of double quote '"' and +// backslash '\\' +// characters. +// Type: Method. +// Args: None. +// Return: CMIUtilString - The wrapped version of the initial string. +// Throws: None. +//-- +CMIUtilString CMIUtilString::AddSlashes() const { + const char cBckSlash('\\'); + const size_t nLen(length()); + CMIUtilString strNew; + strNew.reserve(nLen); + + size_t nOffset(0); + while (nOffset < nLen) { + const size_t nUnescapedCharPos(find_first_of("\"\\", nOffset)); + const bool bUnescapedCharNotFound(nUnescapedCharPos == std::string::npos); + if (bUnescapedCharNotFound) { + const size_t nAppendAll(std::string::npos); + strNew.append(*this, nOffset, nAppendAll); + break; + } + const size_t nAppendLen(nUnescapedCharPos - nOffset); + strNew.append(*this, nOffset, nAppendLen); + strNew.push_back(cBckSlash); + const char cUnescapedChar((*this)[nUnescapedCharPos]); + strNew.push_back(cUnescapedChar); + nOffset = nUnescapedCharPos + 1; + } + + return strNew; +} + +//++ +// Details: Remove backslashes added by CMIUtilString::AddSlashes. +// Type: Method. +// Args: None. +// Return: CMIUtilString - The initial version of wrapped string. +// Throws: None. +//-- +CMIUtilString CMIUtilString::StripSlashes() const { + const char cBckSlash('\\'); + const size_t nLen(length()); + CMIUtilString strNew; + strNew.reserve(nLen); + + size_t nOffset(0); + while (nOffset < nLen) { + const size_t nBckSlashPos(find(cBckSlash, nOffset)); + const bool bBckSlashNotFound(nBckSlashPos == std::string::npos); + if (bBckSlashNotFound) { + const size_t nAppendAll(std::string::npos); + strNew.append(*this, nOffset, nAppendAll); + break; + } + const size_t nAppendLen(nBckSlashPos - nOffset); + strNew.append(*this, nOffset, nAppendLen); + const bool bBckSlashIsLast(nBckSlashPos == nLen); + if (bBckSlashIsLast) { + strNew.push_back(cBckSlash); + break; + } + const char cEscapedChar((*this)[nBckSlashPos + 1]); + const size_t nEscapedCharPos(std::string("\"\\").find(cEscapedChar)); + const bool bEscapedCharNotFound(nEscapedCharPos == std::string::npos); + if (bEscapedCharNotFound) + strNew.push_back(cBckSlash); + strNew.push_back(cEscapedChar); + nOffset = nBckSlashPos + 2; + } + + return strNew; +} + +CMIUtilString CMIUtilString::ConvertToPrintableASCII(const char vChar, + bool bEscapeQuotes) { + switch (vChar) { + case '\a': + return "\\a"; + case '\b': + return "\\b"; + case '\t': + return "\\t"; + case '\n': + return "\\n"; + case '\v': + return "\\v"; + case '\f': + return "\\f"; + case '\r': + return "\\r"; + case '\033': + return "\\e"; + case '\\': + return "\\\\"; + case '"': + if (bEscapeQuotes) + return "\\\""; + LLVM_FALLTHROUGH; + default: + if (::isprint(vChar)) + return Format("%c", vChar); + else + return Format("\\x%02" PRIx8, vChar); + } +} + +CMIUtilString +CMIUtilString::ConvertCharValueToPrintableASCII(char vChar, + bool bEscapeQuotes) { + switch (vChar) { + case '\a': + return "\\a"; + case '\b': + return "\\b"; + case '\t': + return "\\t"; + case '\n': + return "\\n"; + case '\v': + return "\\v"; + case '\f': + return "\\f"; + case '\r': + return "\\r"; + case '\033': + return "\\e"; + case '\\': + return "\\\\"; + case '"': + if (bEscapeQuotes) + return "\\\""; + LLVM_FALLTHROUGH; + default: + if (::isprint(vChar)) + return Format("%c", vChar); + else + return CMIUtilString(); + } +} + +CMIUtilString CMIUtilString::ConvertToPrintableASCII(const char16_t vChar16, + bool bEscapeQuotes) { + if (vChar16 == (char16_t)(char)vChar16) { + // Convert char16_t to char (if possible) + CMIUtilString str = + ConvertCharValueToPrintableASCII((char)vChar16, bEscapeQuotes); + if (str.length() > 0) + return str; + } + return Format("\\u%02" PRIx8 "%02" PRIx8, (vChar16 >> 8) & 0xff, + vChar16 & 0xff); +} + +CMIUtilString CMIUtilString::ConvertToPrintableASCII(const char32_t vChar32, + bool bEscapeQuotes) { + if (vChar32 == (char32_t)(char)vChar32) { + // Convert char32_t to char (if possible) + CMIUtilString str = + ConvertCharValueToPrintableASCII((char)vChar32, bEscapeQuotes); + if (str.length() > 0) + return str; + } + return Format("\\U%02" PRIx8 "%02" PRIx8 "%02" PRIx8 "%02" PRIx8, + (vChar32 >> 24) & 0xff, (vChar32 >> 16) & 0xff, + (vChar32 >> 8) & 0xff, vChar32 & 0xff); +} diff --git a/src/MIUtilString.h b/src/MIUtilString.h new file mode 100644 index 00000000000..e9d3af7a622 --- /dev/null +++ b/src/MIUtilString.h @@ -0,0 +1,95 @@ +//===-- MIUtilString.h ------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// Third party headers: +#include +#include +#include +#include + +// In-house headers: +#include "MIDataTypes.h" + +//++ +//============================================================================ +// Details: MI common code utility class. Used to help handle text. +// Derived from std::string +//-- +class CMIUtilString : public std::string { + // Typedefs: +public: + typedef std::vector VecString_t; + + // Static method: +public: + static CMIUtilString Format(const char *vFormating, ...); + static CMIUtilString FormatBinary(const MIuint64 vnDecimal); + static CMIUtilString FormatValist(const CMIUtilString &vrFormating, + va_list vArgs); + static bool IsAllValidAlphaAndNumeric(const char *vpText); + static const char *WithNullAsEmpty(const char *vpText) { + return vpText ? vpText : ""; + } + static bool Compare(const CMIUtilString &vrLhs, const CMIUtilString &vrRhs); + static CMIUtilString ConvertToPrintableASCII(const char vChar, + bool bEscapeQuotes = false); + static CMIUtilString ConvertToPrintableASCII(const char16_t vChar16, + bool bEscapeQuotes = false); + static CMIUtilString ConvertToPrintableASCII(const char32_t vChar32, + bool bEscapeQuotes = false); + + // Methods: +public: + /* ctor */ CMIUtilString(); + /* ctor */ CMIUtilString(const char *vpData); + /* ctor */ CMIUtilString(const std::string &vrStr); + // + bool ExtractNumber(MIint64 &vwrNumber) const; + CMIUtilString FindAndReplace(const CMIUtilString &vFind, + const CMIUtilString &vReplaceWith) const; + bool IsNumber() const; + bool IsHexadecimalNumber() const; + bool IsQuoted() const; + CMIUtilString RemoveRepeatedCharacters(const char vChar); + size_t Split(const CMIUtilString &vDelimiter, VecString_t &vwVecSplits) const; + size_t SplitConsiderQuotes(const CMIUtilString &vDelimiter, + VecString_t &vwVecSplits) const; + size_t SplitLines(VecString_t &vwVecSplits) const; + CMIUtilString StripCREndOfLine() const; + CMIUtilString StripCRAll() const; + CMIUtilString Trim() const; + CMIUtilString Trim(const char vChar) const; + size_t FindFirst(const CMIUtilString &vrPattern, size_t vnPos = 0) const; + size_t FindFirst(const CMIUtilString &vrPattern, bool vbSkipQuotedText, + bool &vrwbNotFoundClosedQuote, size_t vnPos = 0) const; + size_t FindFirstNot(const CMIUtilString &vrPattern, size_t vnPos = 0) const; + CMIUtilString Escape(bool vbEscapeQuotes = false) const; + CMIUtilString AddSlashes() const; + CMIUtilString StripSlashes() const; + // + CMIUtilString &operator=(const char *vpRhs); + CMIUtilString &operator=(const std::string &vrRhs); + + // Overrideable: +public: + /* dtor */ virtual ~CMIUtilString(); + + // Static method: +private: + static CMIUtilString FormatPriv(const CMIUtilString &vrFormat, va_list vArgs); + static CMIUtilString ConvertCharValueToPrintableASCII(char vChar, + bool bEscapeQuotes); + + // Methods: +private: + bool ExtractNumberFromHexadecimal(MIint64 &vwrNumber) const; + CMIUtilString RemoveRepeatedCharacters(size_t vnPos, const char vChar); + size_t FindFirstQuote(size_t vnPos) const; +}; diff --git a/src/MIUtilThreadBaseStd.cpp b/src/MIUtilThreadBaseStd.cpp new file mode 100644 index 00000000000..72cf1474a78 --- /dev/null +++ b/src/MIUtilThreadBaseStd.cpp @@ -0,0 +1,302 @@ +//===-- MIUtilThreadBaseStd.cpp ---------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// Third Party Headers: +#include + +// In-house headers: +#include "MICmnThreadMgrStd.h" +#include "MIUtilThreadBaseStd.h" + +//++ +// Details: Constructor. +// Type: None. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIUtilThreadActiveObjBase::CMIUtilThreadActiveObjBase() + : m_references(0), m_bHasBeenKilled(false) {} + +//++ +// Details: Destructor. +// Type: None. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIUtilThreadActiveObjBase::~CMIUtilThreadActiveObjBase() { + // Make sure our thread is not alive before we die + m_thread.Join(); +} + +//++ +// Details: Check if an object is already running. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIUtilThreadActiveObjBase::ThreadIsActive() { + // Create a new thread to occupy this threads Run() function + return m_thread.IsActive(); +} + +//++ +// Details: Set up *this thread. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIUtilThreadActiveObjBase::ThreadExecute() { + // Create a new thread to occupy this threads Run() function + return m_thread.Start(ThreadEntry, this); +} + +//++ +// Details: Acquire a reference to CMIUtilThreadActiveObjBase. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIUtilThreadActiveObjBase::Acquire() { + // Access to this function is serial + CMIUtilThreadLock serial(m_mutex); + + // >0 == *this thread is alive + m_references++; + + return MIstatus::success; +} + +//++ +// Details: Release a reference to CMIUtilThreadActiveObjBase. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIUtilThreadActiveObjBase::Release() { + // Access to this function is serial + CMIUtilThreadLock serial(m_mutex); + + // 0 == kill off *this thread + m_references--; + + return MIstatus::success; +} + +//++ +// Details: Force this thread to stop, regardless of references +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIUtilThreadActiveObjBase::ThreadKill() { + // Access to this function is serial + CMIUtilThreadLock serial(m_mutex); + + // Set this thread to killed status + m_bHasBeenKilled = true; + + return MIstatus::success; +} + +//++ +// Details: Proxy to thread join. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIUtilThreadActiveObjBase::ThreadJoin() { return m_thread.Join(); } + +//++ +// Details: This function is the entry point of this object thread. +// It is a trampoline to an instances operation manager. +// Type: Static method. +// Args: vpThisClass - (R) From the system (our CMIUtilThreadActiveObjBase +// from the ctor). +// Return: MIuint - 0 = success. +// Throws: None. +//-- +MIuint CMIUtilThreadActiveObjBase::ThreadEntry(void *vpThisClass) { + // The argument is a pointer to a CMIUtilThreadActiveObjBase class + // as passed from the initialize function, so we can safely cast it. + assert(vpThisClass != nullptr); + CMIUtilThreadActiveObjBase *pActive = + reinterpret_cast(vpThisClass); + + // Start the management routine of this object + pActive->ThreadManage(); + + // Thread death + return 0; +} + +//++ +// Details: This function forms a small management routine, to handle the +// thread's running. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMIUtilThreadActiveObjBase::ThreadManage() { + bool bAlive = true; + + // Infinite loop + while (bAlive) { + // Scope the lock while we access m_isDying + { + // Lock down access to the interface + CMIUtilThreadLock serial(m_mutex); + + // Quit the run loop if we are dying + if (m_references == 0) + break; + } + // Execute the run routine + if (!ThreadRun(bAlive)) + // Thread's run function failed (MIstatus::failure) + break; + + // We will die if we have been signaled to die + bAlive &= !m_bHasBeenKilled; + } + + // Execute the finish routine just before we die + // to give the object a chance to clean up + ThreadFinish(); + + m_thread.Finish(); +} + + +// +CMIUtilThread::CMIUtilThread() : m_pThread(nullptr), m_bIsActive(false) {} + +//++ +// Details: CMIUtilThread destructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIUtilThread::~CMIUtilThread() { Join(); } + +//++ +// Details: Wait for thread to stop. +// Type: Method. +// Args: None. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIUtilThread::Join() { + if (m_pThread != nullptr) { + // Wait for this thread to die + m_pThread->join(); + + // Scope the thread lock while we modify the pointer + { + CMIUtilThreadLock _lock(m_mutex); + delete m_pThread; + m_pThread = nullptr; + } + } + + return MIstatus::success; +} + +//++ +// Details: Is the thread doing work. +// Type: Method. +// Args: None. +// Return: bool - True = Yes active, false = not active. +// Throws: None. +//-- +bool CMIUtilThread::IsActive() { + // Lock while we access the thread status + CMIUtilThreadLock _lock(m_mutex); + return m_bIsActive; +} + +//++ +// Details: Finish this thread +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMIUtilThread::Finish() { + // Lock while we access the thread status + CMIUtilThreadLock _lock(m_mutex); + m_bIsActive = false; +} + +//++ +// Details: Set up *this thread. +// Type: Method. +// Args: vpFn (R) - Function pointer to thread's main function. +// vpArg (R) - Pointer arguments to pass to the thread. +// Return: MIstatus::success - Functional succeeded. +// MIstatus::failure - Functional failed. +// Throws: None. +//-- +bool CMIUtilThread::Start(FnThreadProc vpFn, void *vpArg) { + // Lock while we access the thread pointer and status + CMIUtilThreadLock _lock(m_mutex); + + // Create the std thread, which starts immediately and update its status + m_pThread = new std::thread(vpFn, vpArg); + m_bIsActive = true; + + // We expect to always be able to create one + assert(m_pThread != nullptr); + + return MIstatus::success; +} + + +//++ +// Details: Take resource. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMIUtilThreadMutex::Lock() { m_mutex.lock(); } + +//++ +// Details: Release resource. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMIUtilThreadMutex::Unlock() { m_mutex.unlock(); } + +//++ +// Details: Take resource if available. Immediately return in either case. +// Type: Method. +// Args: None. +// Return: True - mutex has been locked. +// False - mutex could not be locked. +// Throws: None. +//-- +bool CMIUtilThreadMutex::TryLock() { return m_mutex.try_lock(); } diff --git a/src/MIUtilThreadBaseStd.h b/src/MIUtilThreadBaseStd.h new file mode 100644 index 00000000000..3fa03b6f072 --- /dev/null +++ b/src/MIUtilThreadBaseStd.h @@ -0,0 +1,140 @@ +//===-- MIUtilThreadBaseStd.h -----------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// Third party headers: +#include +#include + +// In-house headers: +#include "MIDataTypes.h" +#include "MIUtilString.h" + +//++ +//============================================================================ +// Details: MI common code utility class. Handle thread mutual exclusion. +// Embed Mutexes in your Active Object and then use them through Locks. +//-- +class CMIUtilThreadMutex { + // Methods: +public: + /* ctor */ CMIUtilThreadMutex() {} + // + void Lock(); // Wait until mutex can be obtained + void Unlock(); // Release the mutex + bool TryLock(); // Gain the lock if available + + // Overrideable: +public: + // From CMICmnBase + /* dtor */ virtual ~CMIUtilThreadMutex() {} + + // Attributes: +private: + std::recursive_mutex m_mutex; +}; + +//++ +//============================================================================ +// Details: MI common code utility class. Thread object. +//-- +class CMIUtilThread { + // Typedef: +public: + typedef MIuint (*FnThreadProc)(void *vpThisClass); + + // Methods: +public: + /* ctor */ CMIUtilThread(); + // + bool Start(FnThreadProc vpFn, void *vpArg); // Start execution of this thread + bool Join(); // Wait for this thread to stop + bool IsActive(); // Returns true if this thread is running + void Finish(); // Finish this thread + + // Overrideable: +public: + /* dtor */ virtual ~CMIUtilThread(); + + // Methods: +private: + CMIUtilThreadMutex m_mutex; + std::thread *m_pThread; + bool m_bIsActive; +}; + +//++ +//============================================================================ +// Details: MI common code utility class. Base class for a worker thread active +// object. Runs an 'captive thread'. +//-- +class CMIUtilThreadActiveObjBase { + // Methods: +public: + /* ctor */ CMIUtilThreadActiveObjBase(); + // + bool Acquire(); // Obtain a reference to this object + bool Release(); // Release a reference to this object + bool ThreadIsActive(); // Return true if this object is running + bool ThreadJoin(); // Wait for this thread to stop running + bool ThreadKill(); // Force this thread to stop, regardless of references + bool ThreadExecute(); // Start this objects execution in another thread + void ThreadManage(); + + // Overrideable: +public: + /* dtor */ virtual ~CMIUtilThreadActiveObjBase(); + // + // Each thread object must supple a unique name that can be used to locate it + virtual const CMIUtilString &ThreadGetName() const = 0; + + // Statics: +protected: + static MIuint ThreadEntry(void *vpThisClass); // Thread entry point + + // Overrideable: +protected: + virtual bool ThreadRun(bool &vrIsAlive) = 0; // Call the main worker method + virtual bool ThreadFinish() = 0; // Finish of what you were doing + + // Attributes: +protected: + volatile MIuint m_references; // Stores the current lifetime state of this + // thread, 0 = running, > 0 = shutting down + volatile bool + m_bHasBeenKilled; // Set to true when this thread has been killed + CMIUtilThread m_thread; // The execution thread + CMIUtilThreadMutex m_mutex; // This mutex allows us to safely communicate with + // this thread object across the interface from + // multiple threads +}; + +//++ +//============================================================================ +// Details: MI common code utility class. Handle thread resource locking. +// Put Locks inside all the methods of your Active Object that access +// data shared with the captive thread. +//-- +class CMIUtilThreadLock { + // Methods: +public: + /* ctor */ + CMIUtilThreadLock(CMIUtilThreadMutex &vMutex) : m_rMutex(vMutex) { + m_rMutex.Lock(); + } + + // Overrideable: +public: + /* dtor */ + virtual ~CMIUtilThreadLock() { m_rMutex.Unlock(); } + + // Attributes: +private: + CMIUtilThreadMutex &m_rMutex; +}; diff --git a/src/MIUtilVariant.cpp b/src/MIUtilVariant.cpp new file mode 100644 index 00000000000..062dce80c4b --- /dev/null +++ b/src/MIUtilVariant.cpp @@ -0,0 +1,344 @@ +//===-- MIUtilVariant.cpp----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// In-house headers: +#include "MIUtilVariant.h" + +//++ +// Details: CDataObjectBase constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIUtilVariant::CDataObjectBase::CDataObjectBase() {} + +//++ +// Details: CDataObjectBase copy constructor. +// Type: Method. +// Args: vrOther - (R) The other object. +// Return: None. +// Throws: None. +//-- +CMIUtilVariant::CDataObjectBase::CDataObjectBase( + const CDataObjectBase &vrOther) { + MIunused(vrOther); +} + +//++ +// Details: CDataObjectBase copy constructor. +// Type: Method. +// Args: vrOther - (R) The other object. +// Return: None. +// Throws: None. +//-- +CMIUtilVariant::CDataObjectBase::CDataObjectBase(CDataObjectBase &vrOther) { + MIunused(vrOther); +} + +//++ +// Details: CDataObjectBase move constructor. +// Type: Method. +// Args: vrwOther - (R) The other object. +// Return: None. +// Throws: None. +//-- +CMIUtilVariant::CDataObjectBase::CDataObjectBase(CDataObjectBase &&vrwOther) { + MIunused(vrwOther); +} + +//++ +// Details: CDataObjectBase destructor. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIUtilVariant::CDataObjectBase::~CDataObjectBase() { Destroy(); } + +//++ +// Details: CDataObjectBase copy assignment. +// Type: Method. +// Args: vrOther - (R) The other object. +// Return: None. +// Throws: None. +//-- +CMIUtilVariant::CDataObjectBase &CMIUtilVariant::CDataObjectBase:: +operator=(const CDataObjectBase &vrOther) { + Copy(vrOther); + return *this; +} + +//++ +// Details: CDataObjectBase move assignment. +// Type: Method. +// Args: vrwOther - (R) The other object. +// Return: None. +// Throws: None. +//-- +CMIUtilVariant::CDataObjectBase &CMIUtilVariant::CDataObjectBase:: +operator=(CDataObjectBase &&vrwOther) { + Copy(vrwOther); + vrwOther.Destroy(); + return *this; +} + +//++ +// Details: Create a new copy of *this class. +// Type: Overrideable. +// Args: None. +// Return: CDataObjectBase * - Pointer to a new object. +// Throws: None. +//-- +CMIUtilVariant::CDataObjectBase * +CMIUtilVariant::CDataObjectBase::CreateCopyOfSelf() { + // Override to implement copying of variant's data object + return new CDataObjectBase(); +} + +//++ +// Details: Determine if *this object is a derived from CDataObjectBase. +// Type: Overrideable. +// Args: None. +// Return: bool - True = *this is derived from CDataObjectBase, false = +// *this is instance of the this base class. +// Throws: None. +//-- +bool CMIUtilVariant::CDataObjectBase::GetIsDerivedClass() const { + // Override to in the derived class and return true + return false; +} + +//++ +// Details: Perform a bitwise copy of *this object. +// Type: Overrideable. +// Args: vrOther - (R) The other object. +// Return: None. +// Throws: None. +//-- +void CMIUtilVariant::CDataObjectBase::Copy(const CDataObjectBase &vrOther) { + // Override to implement + MIunused(vrOther); +} + +//++ +// Details: Release any resources used by *this object. +// Type: Overrideable. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMIUtilVariant::CDataObjectBase::Destroy() { + // Do nothing - override to implement +} + + +//++ +// Details: CDataObject copy constructor. +// Type: Method. +// Args: T - The object's type. +// vrOther - (R) The other object. +// Return: None. +// Throws: None. +//-- +template +CMIUtilVariant::CDataObject::CDataObject(const CDataObject &vrOther) { + if (this == &vrOther) + return; + Copy(vrOther); +} + +//++ +// Details: CDataObject copy constructor. +// Type: Method. +// Args: T - The object's type. +// vrOther - (R) The other object. +// Return: None. +// Throws: None. +//-- +template +CMIUtilVariant::CDataObject::CDataObject(CDataObject &vrOther) { + if (this == &vrOther) + return; + Copy(vrOther); +} + +//++ +// Details: CDataObject move constructor. +// Type: Method. +// Args: T - The object's type. +// vrwOther - (R) The other object. +// Return: None. +// Throws: None. +//-- +template +CMIUtilVariant::CDataObject::CDataObject(CDataObject &&vrwOther) { + if (this == &vrwOther) + return; + Copy(vrwOther); + vrwOther.Destroy(); +} + +//++ +// Details: CDataObject copy assignment. +// Type: Method. +// Args: T - The object's type. +// vrOther - (R) The other object. +// Return: None. +// Throws: None. +//-- +template +CMIUtilVariant::CDataObject &CMIUtilVariant::CDataObject:: +operator=(const CDataObject &vrOther) { + if (this == &vrOther) + return *this; + Copy(vrOther); + return *this; +} + +//++ +// Details: CDataObject move assignment. +// Type: Method. +// Args: T - The object's type. +// vrwOther - (R) The other object. +// Return: None. +// Throws: None. +//-- +template +CMIUtilVariant::CDataObject &CMIUtilVariant::CDataObject:: +operator=(CDataObject &&vrwOther) { + if (this == &vrwOther) + return *this; + Copy(vrwOther); + vrwOther.Destroy(); + return *this; +} + + +//++ +// Details: CMIUtilVariant constructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIUtilVariant::CMIUtilVariant() : m_pDataObject(nullptr) {} + +//++ +// Details: CMIUtilVariant copy constructor. +// Type: Method. +// Args: vrOther - (R) The other object. +// Return: None. +// Throws: None. +//-- +CMIUtilVariant::CMIUtilVariant(const CMIUtilVariant &vrOther) + : m_pDataObject(nullptr) { + if (this == &vrOther) + return; + + Copy(vrOther); +} + +//++ +// Details: CMIUtilVariant copy constructor. +// Type: Method. +// Args: vrOther - (R) The other object. +// Return: None. +// Throws: None. +//-- +CMIUtilVariant::CMIUtilVariant(CMIUtilVariant &vrOther) + : m_pDataObject(nullptr) { + if (this == &vrOther) + return; + + Copy(vrOther); +} + +//++ +// Details: CMIUtilVariant move constructor. +// Type: Method. +// Args: vrwOther - (R) The other object. +// Return: None. +// Throws: None. +//-- +CMIUtilVariant::CMIUtilVariant(CMIUtilVariant &&vrwOther) + : m_pDataObject(nullptr) { + if (this == &vrwOther) + return; + + Copy(vrwOther); + vrwOther.Destroy(); +} + +//++ +// Details: CMIUtilVariant destructor. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +CMIUtilVariant::~CMIUtilVariant() { Destroy(); } + +//++ +// Details: CMIUtilVariant copy assignment. +// Type: Method. +// Args: vrOther - (R) The other object. +// Return: None. +// Throws: None. +//-- +CMIUtilVariant &CMIUtilVariant::operator=(const CMIUtilVariant &vrOther) { + if (this == &vrOther) + return *this; + + Copy(vrOther); + return *this; +} + +//++ +// Details: CMIUtilVariant move assignment. +// Type: Method. +// Args: vrwOther - (R) The other object. +// Return: None. +// Throws: None. +//-- +CMIUtilVariant &CMIUtilVariant::operator=(CMIUtilVariant &&vrwOther) { + if (this == &vrwOther) + return *this; + + Copy(vrwOther); + vrwOther.Destroy(); + return *this; +} + +//++ +// Details: Release the resources used by *this object. +// Type: Method. +// Args: None. +// Return: None. +// Throws: None. +//-- +void CMIUtilVariant::Destroy() { + if (m_pDataObject != nullptr) + delete m_pDataObject; + m_pDataObject = nullptr; +} + +//++ +// Details: Bitwise copy another data object to *this variant object. +// Type: Method. +// Args: vrOther - (R) The other object. +// Return: None. +// Throws: None. +//-- +void CMIUtilVariant::Copy(const CMIUtilVariant &vrOther) { + Destroy(); + + if (vrOther.m_pDataObject != nullptr) { + m_pDataObject = vrOther.m_pDataObject->CreateCopyOfSelf(); + } +} diff --git a/src/MIUtilVariant.h b/src/MIUtilVariant.h new file mode 100644 index 00000000000..4c9db33021b --- /dev/null +++ b/src/MIUtilVariant.h @@ -0,0 +1,247 @@ +//===-- MIUtilVariant.h -----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#pragma once + +// In-house headers: +#include "MIDataTypes.h" + +//++ +//============================================================================ +// Details: MI common code utility class. The class implements behaviour of a +// variant object which holds any data object of type T. A copy of the +// data object specified is made and stored in *this wrapper. When the +// *this object is destroyed the data object hold within calls its +// destructor should it have one. +//-- +class CMIUtilVariant { + // Methods: +public: + /* ctor */ CMIUtilVariant(); + /* ctor */ CMIUtilVariant(const CMIUtilVariant &vrOther); + /* ctor */ CMIUtilVariant(CMIUtilVariant &vrOther); + /* ctor */ CMIUtilVariant(CMIUtilVariant &&vrwOther); + /* dtor */ ~CMIUtilVariant(); + + template void Set(const T &vArg); + template T *Get() const; + + CMIUtilVariant &operator=(const CMIUtilVariant &vrOther); + CMIUtilVariant &operator=(CMIUtilVariant &&vrwOther); + + // Classes: +private: + //++ ---------------------------------------------------------------------- + // Details: Base class wrapper to hold the variant's data object when + // assigned to it by the Set() function. Do not use the + // CDataObjectBase + // to create objects, use only CDataObjectBase derived objects, + // see CDataObject() class. + //-- + class CDataObjectBase { + // Methods: + public: + /* ctor */ CDataObjectBase(); + /* ctor */ CDataObjectBase(const CDataObjectBase &vrOther); + /* ctor */ CDataObjectBase(CDataObjectBase &vrOther); + /* ctor */ CDataObjectBase(CDataObjectBase &&vrwOther); + // + CDataObjectBase &operator=(const CDataObjectBase &vrOther); + CDataObjectBase &operator=(CDataObjectBase &&vrwOther); + + // Overrideable: + public: + virtual ~CDataObjectBase(); + virtual CDataObjectBase *CreateCopyOfSelf(); + virtual bool GetIsDerivedClass() const; + + // Overrideable: + protected: + virtual void Copy(const CDataObjectBase &vrOther); + virtual void Destroy(); + }; + + //++ ---------------------------------------------------------------------- + // Details: Derived from CDataObjectBase, this class is the wrapper for the + // data object as it has an aggregate of type T which is a copy + // of the data object assigned to the variant object. + //-- + template class CDataObject : public CDataObjectBase { + // Methods: + public: + /* ctor */ CDataObject(); + /* ctor */ CDataObject(const T &vArg); + /* ctor */ CDataObject(const CDataObject &vrOther); + /* ctor */ CDataObject(CDataObject &vrOther); + /* ctor */ CDataObject(CDataObject &&vrwOther); + // + CDataObject &operator=(const CDataObject &vrOther); + CDataObject &operator=(CDataObject &&vrwOther); + // + T &GetDataObject(); + + // Overridden: + public: + // From CDataObjectBase + ~CDataObject() override; + CDataObjectBase *CreateCopyOfSelf() override; + bool GetIsDerivedClass() const override; + + // Overrideable: + private: + virtual void Duplicate(const CDataObject &vrOther); + + // Overridden: + private: + // From CDataObjectBase + void Destroy() override; + + // Attributes: + private: + T m_dataObj; + }; + + // Methods +private: + void Destroy(); + void Copy(const CMIUtilVariant &vrOther); + + // Attributes: +private: + CDataObjectBase *m_pDataObject; +}; + + +//++ +// Details: CDataObject constructor. +// Type: Method. +// Args: T - The object's type. +// Return: None. +// Throws: None. +//-- +template CMIUtilVariant::CDataObject::CDataObject() {} + +//++ +// Details: CDataObject constructor. +// Type: Method. +// Args: T - The object's type. +// vArg - (R) The data object to be stored in the variant object. +// Return: None. +// Throws: None. +//-- +template +CMIUtilVariant::CDataObject::CDataObject(const T &vArg) { + m_dataObj = vArg; +} + +//++ +// Details: CDataObject destructor. +// Type: Overridden. +// Args: T - The object's type. +// Return: None. +// Throws: None. +//-- +template CMIUtilVariant::CDataObject::~CDataObject() { + Destroy(); +} + +//++ +// Details: Retrieve the data object hold by *this object wrapper. +// Type: Method. +// Args: T - The object's type. +// Return: T & - Reference to the data object. +// Throws: None. +//-- +template T &CMIUtilVariant::CDataObject::GetDataObject() { + return m_dataObj; +} + +//++ +// Details: Create a new copy of *this class. +// Type: Overridden. +// Args: T - The object's type. +// Return: CDataObjectBase * - Pointer to a new object. +// Throws: None. +//-- +template +CMIUtilVariant::CDataObjectBase * +CMIUtilVariant::CDataObject::CreateCopyOfSelf() { + CDataObject *pCopy = new CDataObject(m_dataObj); + + return pCopy; +} + +//++ +// Details: Determine if *this object is a derived from CDataObjectBase. +// Type: Overridden. +// Args: T - The object's type. +// Return: bool - True = *this is derived from CDataObjectBase +// - False = *this is an instance of the base class. +// Throws: None. +//-- +template +bool CMIUtilVariant::CDataObject::GetIsDerivedClass() const { + return true; +} + +//++ +// Details: Perform a bitwise copy of *this object. +// Type: Overrideable. +// Args: T - The object's type. +// vrOther - (R) The other object. +// Return: None. +// Throws: None. +//-- +template +void CMIUtilVariant::CDataObject::Duplicate(const CDataObject &vrOther) { + CDataObjectBase::Copy(vrOther); + m_dataObj = vrOther.m_dataObj; +} + +//++ +// Details: Release any resources used by *this object. +// Type: Overridden. +// Args: None. +// Return: None. +// Throws: None. +//-- +template void CMIUtilVariant::CDataObject::Destroy() { + CDataObjectBase::Destroy(); +} + + +//++ +// Details: Assign to the variant an object of a specified type. +// Type: Template method. +// Args: T - The object's type. +// vArg - (R) The object to store. +// Return: None. +// Throws: None. +//-- +template void CMIUtilVariant::Set(const T &vArg) { + m_pDataObject = new CDataObject(vArg); +} + +//++ +// Details: Retrieve the data object from *this variant. +// Type: Template method. +// Args: T - The object's type. +// Return: T * - Pointer the data object, NULL = data object not assigned to +// *this variant. +// Throws: None. +//-- +template T *CMIUtilVariant::Get() const { + if ((m_pDataObject != nullptr) && m_pDataObject->GetIsDerivedClass()) { + CDataObject *pDataObj = static_cast *>(m_pDataObject); + return &pDataObj->GetDataObject(); + } + + // Do not use a CDataObjectBase object, use only CDataObjectBase derived + // objects + return nullptr; +} diff --git a/src/Platform.h b/src/Platform.h new file mode 100644 index 00000000000..f2885e12e17 --- /dev/null +++ b/src/Platform.h @@ -0,0 +1,91 @@ +//===-- Platform.h ----------------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// 2021.3.15 Add support for OHOS target. +// Copyright (c) 2021 Huawei Device Co., Ltd. All rights reserved. +// +//===----------------------------------------------------------------------===// +#pragma once + +#if defined(_MSC_VER) || defined(_WIN32) + +#include +#include +#include + +#include "lldb/Host/HostGetOpt.h" +#include "lldb/Host/windows/windows.h" + +struct winsize { + long ws_col; +}; + +typedef unsigned char cc_t; +typedef unsigned int speed_t; +typedef unsigned int tcflag_t; + +// fcntl.h // This is not used by MI +#define O_NOCTTY 0400 + +// ioctls.h +#define TIOCGWINSZ 0x5413 + +// tcsetattr arguments +#define TCSANOW 0 + +#define NCCS 32 +struct termios { + tcflag_t c_iflag; // input mode flags + tcflag_t c_oflag; // output mode flags + tcflag_t c_cflag; // control mode flags + tcflag_t c_lflag; // local mode flags + cc_t c_line; // line discipline + cc_t c_cc[NCCS]; // control characters + speed_t c_ispeed; // input speed + speed_t c_ospeed; // output speed +}; + +#ifdef _MSC_VER +typedef long pid_t; +#endif + +#define STDIN_FILENO 0 +#define PATH_MAX 32768 +#define snprintf _snprintf + +extern int ioctl(int d, int request, ...); +extern int kill(pid_t pid, int sig); +extern int tcsetattr(int fd, int optional_actions, + const struct termios *termios_p); +extern int tcgetattr(int fildes, struct termios *termios_p); + +// signal handler function pointer type +typedef void (*sighandler_t)(int); + +// CODETAG_IOR_SIGNALS +// signal.h +#define SIGQUIT 3 // Terminal quit signal +#define SIGKILL 9 // Kill (cannot be caught or ignored) +#define SIGPIPE 13 // Write on a pipe with no one to read it +#define SIGCONT 18 // Continue executing, if stopped. +#define SIGTSTP 20 // Terminal stop signal +#define SIGSTOP 23 // Stop executing (cannot be caught or ignored) +#define SIGWINCH 28 // (== SIGVTALRM) + +#else + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#endif diff --git a/src/lldb-Info.plist b/src/lldb-Info.plist new file mode 100644 index 00000000000..795512691ef --- /dev/null +++ b/src/lldb-Info.plist @@ -0,0 +1,21 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleIdentifier + com.apple.lldb + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + lldb-mi + CFBundleVersion + 2 + SecTaskAccess + + allowed + debug + + + diff --git a/src/lldb-mi.exports b/src/lldb-mi.exports new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/module.modulemap b/src/module.modulemap new file mode 100644 index 00000000000..01f6a2b7950 --- /dev/null +++ b/src/module.modulemap @@ -0,0 +1,79 @@ +module lldb_mi { + module MICmdArgContext { header "MICmdArgContext.h" export * } + module MICmdArgSet { header "MICmdArgSet.h" export * } + module MICmdArgValBase { header "MICmdArgValBase.h" export * } + module MICmdArgValConsume { header "MICmdArgValConsume.h" export * } + module MICmdArgValFile { header "MICmdArgValFile.h" export * } + module MICmdArgValListBase { header "MICmdArgValListBase.h" export * } + module MICmdArgValListOfN { header "MICmdArgValListOfN.h" export * } + module MICmdArgValNumber { header "MICmdArgValNumber.h" export * } + module MICmdArgValOptionLong { header "MICmdArgValOptionLong.h" export * } + module MICmdArgValOptionShort { header "MICmdArgValOptionShort.h" export * } + module MICmdArgValPrintValues { header "MICmdArgValPrintValues.h" export * } + module MICmdArgValString { header "MICmdArgValString.h" export * } + module MICmdArgValThreadGrp { header "MICmdArgValThreadGrp.h" export * } + module MICmdBase { header "MICmdBase.h" export * } + module MICmdCmdBreak { header "MICmdCmdBreak.h" export * } + module MICmdCmdData { header "MICmdCmdData.h" export * } + module MICmdCmdEnviro { header "MICmdCmdEnviro.h" export * } + module MICmdCmdExec { header "MICmdCmdExec.h" export * } + module MICmdCmdFile { header "MICmdCmdFile.h" export * } + module MICmdCmdGdbInfo { header "MICmdCmdGdbInfo.h" export * } + module MICmdCmdGdbSet { header "MICmdCmdGdbSet.h" export * } + module MICmdCmdGdbShow { header "MICmdCmdGdbShow.h" export * } + module MICmdCmdGdbThread { header "MICmdCmdGdbThread.h" export * } + module MICmdCmd { header "MICmdCmd.h" export * } + module MICmdCmdMiscellanous { header "MICmdCmdMiscellanous.h" export * } + module MICmdCmdStack { header "MICmdCmdStack.h" export * } + module MICmdCmdSupportInfo { header "MICmdCmdSupportInfo.h" export * } + module MICmdCmdSupportList { header "MICmdCmdSupportList.h" export * } + module MICmdCmdSymbol { header "MICmdCmdSymbol.h" export * } + module MICmdCmdTarget { header "MICmdCmdTarget.h" export * } + module MICmdCmdThread { header "MICmdCmdThread.h" export * } + module MICmdCmdTrace { header "MICmdCmdTrace.h" export * } + module MICmdCmdVar { header "MICmdCmdVar.h" export * } + module MICmdCommands { header "MICmdCommands.h" export * } + module MICmdData { header "MICmdData.h" export * } + module MICmdFactory { header "MICmdFactory.h" export * } + module MICmdInterpreter { header "MICmdInterpreter.h" export * } + module MICmdInvoker { header "MICmdInvoker.h" export * } + module MICmdMgr { header "MICmdMgr.h" export * } + module MICmdMgrSetCmdDeleteCallback { header "MICmdMgrSetCmdDeleteCallback.h" export * } + module MICmnBase { header "MICmnBase.h" export * } + module MICmnConfig { header "MICmnConfig.h" export * } + module MICmnLLDBBroadcaster { header "MICmnLLDBBroadcaster.h" export * } + module MICmnLLDBDebugger { header "MICmnLLDBDebugger.h" export * } + module MICmnLLDBDebuggerHandleEvents { header "MICmnLLDBDebuggerHandleEvents.h" export * } + module MICmnLLDBDebugSessionInfo { header "MICmnLLDBDebugSessionInfo.h" export * } + module MICmnLLDBDebugSessionInfoVarObj { header "MICmnLLDBDebugSessionInfoVarObj.h" export * } + module MICmnLLDBProxySBValue { header "MICmnLLDBProxySBValue.h" export * } + module MICmnLLDBUtilSBValue { header "MICmnLLDBUtilSBValue.h" export * } + module MICmnLog { header "MICmnLog.h" export * } + module MICmnLogMediumFile { header "MICmnLogMediumFile.h" export * } + module MICmnMIOutOfBandRecord { header "MICmnMIOutOfBandRecord.h" export * } + module MICmnMIResultRecord { header "MICmnMIResultRecord.h" export * } + module MICmnMIValueConst { header "MICmnMIValueConst.h" export * } + module MICmnMIValue { header "MICmnMIValue.h" export * } + module MICmnMIValueList { header "MICmnMIValueList.h" export * } + module MICmnMIValueResult { header "MICmnMIValueResult.h" export * } + module MICmnMIValueTuple { header "MICmnMIValueTuple.h" export * } + module MICmnResources { header "MICmnResources.h" export * } + module MICmnStreamStderr { header "MICmnStreamStderr.h" export * } + module MICmnStreamStdin { header "MICmnStreamStdin.h" export * } + module MICmnStreamStdout { header "MICmnStreamStdout.h" export * } + module MICmnThreadMgrStd { header "MICmnThreadMgrStd.h" export * } + module MIDataTypes { header "MIDataTypes.h" export * } + module MIDriverBase { header "MIDriverBase.h" export * } + module MIDriver { header "MIDriver.h" export * } + module MIDriverMgr { header "MIDriverMgr.h" export * } + module MIUtilDateTimeStd { header "MIUtilDateTimeStd.h" export * } + module MIUtilDebug { header "MIUtilDebug.h" export * } + module MIUtilFileStd { header "MIUtilFileStd.h" export * } + module MIUtilMapIdToVariant { header "MIUtilMapIdToVariant.h" export * } + module MIUtilSingletonBase { header "MIUtilSingletonBase.h" export * } + module MIUtilSingletonHelper { header "MIUtilSingletonHelper.h" export * } + module MIUtilString { header "MIUtilString.h" export * } + module MIUtilThreadBaseStd { header "MIUtilThreadBaseStd.h" export * } + module MIUtilVariant { header "MIUtilVariant.h" export * } + module Platform { header "Platform.h" export * } +} -- Gitee