|
14 | 14 | // CMICmdCmdBreakCondition implementation.
|
15 | 15 |
|
16 | 16 | // Third Party Headers:
|
| 17 | +#include "cassert" |
17 | 18 | #include "lldb/API/SBBreakpointLocation.h"
|
| 19 | +#include "lldb/API/SBThread.h" |
18 | 20 |
|
19 | 21 | // In-house headers:
|
20 | 22 | #include "MICmdArgValFile.h"
|
@@ -1128,3 +1130,246 @@ bool CMICmdCmdBreakCondition::UpdateStopPtInfo(
|
1128 | 1130 | sStopPtInfo.m_strCondition = m_strBrkPtExpr;
|
1129 | 1131 | return rSessionInfo.RecordStopPtInfo(sStopPtInfo);
|
1130 | 1132 | }
|
| 1133 | + |
| 1134 | +// Details: CMICmdCmdBreakWatch constructor. |
| 1135 | +// Type: Method. |
| 1136 | +// Args: None. |
| 1137 | +// Return: None. |
| 1138 | +// Throws: None. |
| 1139 | +//-- |
| 1140 | +CMICmdCmdBreakWatch::CMICmdCmdBreakWatch() |
| 1141 | + : m_constStrArgNamedAccessWatchPt("a"), m_constStrArgNamedReadWatchPt("r"), |
| 1142 | + m_constStrArgNamedExpr("expr"), m_stopPtInfo(), m_watchPt() { |
| 1143 | + // Command factory matches this name with that received from the stdin stream |
| 1144 | + m_strMiCmd = "break-watch"; |
| 1145 | + |
| 1146 | + // Required by the CMICmdFactory when registering *this command |
| 1147 | + m_pSelfCreatorFn = &CMICmdCmdBreakWatch::CreateSelf; |
| 1148 | +} |
| 1149 | + |
| 1150 | +//++ |
| 1151 | +// Details: The invoker requires this function. The parses the command line |
| 1152 | +// options arguments to extract values for each of those arguments. |
| 1153 | +// Type: Overridden. |
| 1154 | +// Args: None. |
| 1155 | +// Return: MIstatus::success - Functional succeeded. |
| 1156 | +// MIstatus::failure - Functional failed. |
| 1157 | +// Throws: None. |
| 1158 | +//-- |
| 1159 | +bool CMICmdCmdBreakWatch::ParseArgs() { |
| 1160 | + m_setCmdArgs.Add(new CMICmdArgValOptionShort(m_constStrArgNamedAccessWatchPt, |
| 1161 | + false, true)); |
| 1162 | + m_setCmdArgs.Add( |
| 1163 | + new CMICmdArgValOptionShort(m_constStrArgNamedReadWatchPt, false, true)); |
| 1164 | + m_setCmdArgs.Add(new CMICmdArgValText(m_constStrArgNamedExpr, true, true)); |
| 1165 | + return ParseValidateCmdOptions(); |
| 1166 | +} |
| 1167 | + |
| 1168 | +static bool FindLocalVariableAddress(lldb::SBTarget &rSbTarget, |
| 1169 | + lldb::SBFrame &rSbFrame, |
| 1170 | + const CMIUtilString &rExpression, |
| 1171 | + lldb::addr_t &rAddress, size_t &rSize) { |
| 1172 | + auto sbVariableValue = rSbFrame.GetValueForVariablePath( |
| 1173 | + rExpression.c_str(), lldb::eNoDynamicValues); |
| 1174 | + auto sbAddress = sbVariableValue.GetAddress(); |
| 1175 | + |
| 1176 | + bool isValid = sbVariableValue && sbAddress; |
| 1177 | + if (isValid) { |
| 1178 | + rAddress = sbAddress.GetLoadAddress(rSbTarget); |
| 1179 | + rSize = sbVariableValue.GetByteSize(); |
| 1180 | + } |
| 1181 | + |
| 1182 | + return isValid; |
| 1183 | +} |
| 1184 | + |
| 1185 | +static bool FindGlobalVariableAddress(lldb::SBTarget &rSbTarget, |
| 1186 | + lldb::SBFrame &rSbFrame, |
| 1187 | + const CMIUtilString &rExpression, |
| 1188 | + lldb::addr_t &rAddress, size_t &rSize) { |
| 1189 | + auto sbGlobalVariableValue = |
| 1190 | + rSbTarget.FindFirstGlobalVariable(rExpression.c_str()); |
| 1191 | + if (sbGlobalVariableValue.IsValid()) { |
| 1192 | + auto sbAddress = sbGlobalVariableValue.GetAddress(); |
| 1193 | + if (sbAddress.IsValid()) { |
| 1194 | + rAddress = sbAddress.GetLoadAddress(rSbTarget); |
| 1195 | + rSize = sbGlobalVariableValue.GetByteSize(); |
| 1196 | + return MIstatus::success; |
| 1197 | + } |
| 1198 | + } |
| 1199 | + |
| 1200 | + // In case the previous part didn't succeed, the expression must be something |
| 1201 | + // like "a.b". For locally-visible variables, there is |
| 1202 | + // SBFrame::GetValueForVariablePath that can handle this kind of expressions |
| 1203 | + // but there is no any analogue of this function for global variables. So, we |
| 1204 | + // have to try an address expression at least. |
| 1205 | + auto addressExpression = "&(" + rExpression + ")"; |
| 1206 | + auto sbExpressionValue = |
| 1207 | + rSbFrame.EvaluateExpression(addressExpression.c_str()); |
| 1208 | + |
| 1209 | + lldb::SBError sbError; |
| 1210 | + rAddress = |
| 1211 | + static_cast<lldb::addr_t>(sbExpressionValue.GetValueAsUnsigned(sbError)); |
| 1212 | + if (sbError.Fail()) |
| 1213 | + return false; |
| 1214 | + |
| 1215 | + assert(sbExpressionValue.TypeIsPointerType()); |
| 1216 | + rSize = sbExpressionValue.GetType().GetPointeeType().GetByteSize(); |
| 1217 | + |
| 1218 | + return true; |
| 1219 | +} |
| 1220 | + |
| 1221 | +static bool FindAddressByExpressionEvaluation(lldb::SBTarget &rSbTarget, |
| 1222 | + lldb::SBFrame &rSbFrame, |
| 1223 | + const CMIUtilString &rExpression, |
| 1224 | + lldb::addr_t &rAddress, |
| 1225 | + size_t &rSize) { |
| 1226 | + auto sbExpressionValue = rSbFrame.EvaluateExpression(rExpression.c_str()); |
| 1227 | + |
| 1228 | + lldb::SBError sbError; |
| 1229 | + rAddress = |
| 1230 | + static_cast<lldb::addr_t>(sbExpressionValue.GetValueAsUnsigned(sbError)); |
| 1231 | + if (sbError.Fail()) |
| 1232 | + return false; |
| 1233 | + |
| 1234 | + if (sbExpressionValue.TypeIsPointerType()) |
| 1235 | + rSize = sbExpressionValue.GetType().GetPointeeType().GetByteSize(); |
| 1236 | + else |
| 1237 | + rSize = rSbTarget.GetDataByteSize(); |
| 1238 | + |
| 1239 | + return true; |
| 1240 | +} |
| 1241 | + |
| 1242 | +//++ |
| 1243 | +// Details: The invoker requires this function. The command does work in this |
| 1244 | +// function. The command is likely to communicate with the LLDB |
| 1245 | +// SBDebugger in here. |
| 1246 | +// Type: Overridden. |
| 1247 | +// Args: None. |
| 1248 | +// Return: MIstatus::success - Functional succeeded. |
| 1249 | +// MIstatus::failure - Functional failed. |
| 1250 | +// Throws: None. |
| 1251 | +//-- |
| 1252 | +bool CMICmdCmdBreakWatch::Execute() { |
| 1253 | + CMICMDBASE_GETOPTION(pArgAccess, OptionShort, |
| 1254 | + m_constStrArgNamedAccessWatchPt); |
| 1255 | + CMICMDBASE_GETOPTION(pArgRead, OptionShort, m_constStrArgNamedReadWatchPt); |
| 1256 | + CMICMDBASE_GETOPTION(pArgExpr, Text, m_constStrArgNamedExpr); |
| 1257 | + |
| 1258 | + // Ask LLDB for the target to check if we have valid or dummy one. |
| 1259 | + CMICmnLLDBDebugSessionInfo &rSessionInfo( |
| 1260 | + CMICmnLLDBDebugSessionInfo::Instance()); |
| 1261 | + auto sbTarget = rSessionInfo.GetTarget(); |
| 1262 | + auto sbProcess = rSessionInfo.GetProcess(); |
| 1263 | + auto sbThread = sbProcess.GetSelectedThread(); |
| 1264 | + auto sbFrame = sbThread.GetSelectedFrame(); |
| 1265 | + |
| 1266 | + if (!sbFrame) { |
| 1267 | + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_INVALID_FRAME), |
| 1268 | + m_cmdData.strMiCmd.c_str())); |
| 1269 | + return MIstatus::failure; |
| 1270 | + } |
| 1271 | + |
| 1272 | + lldb::addr_t address; |
| 1273 | + size_t size; |
| 1274 | + |
| 1275 | + bool isVariable = true; |
| 1276 | + auto expression = pArgExpr->GetValue(); |
| 1277 | + if (!FindLocalVariableAddress(sbTarget, sbFrame, expression, address, size) && |
| 1278 | + !FindGlobalVariableAddress(sbTarget, sbFrame, expression, address, |
| 1279 | + size)) { |
| 1280 | + isVariable = false; |
| 1281 | + if (!FindAddressByExpressionEvaluation(sbTarget, sbFrame, expression, |
| 1282 | + address, size)) { |
| 1283 | + SetError(CMIUtilString::Format(MIRSRC(IDS_CMD_ERR_FIND_EXPR_ADDRESS), |
| 1284 | + m_cmdData.strMiCmd.c_str(), |
| 1285 | + expression.c_str())); |
| 1286 | + return MIstatus::failure; |
| 1287 | + } |
| 1288 | + } |
| 1289 | + |
| 1290 | + bool read = pArgAccess->GetFound() || pArgRead->GetFound(); |
| 1291 | + bool write = !pArgRead->GetFound(); |
| 1292 | + |
| 1293 | + lldb::SBError sbError; |
| 1294 | + m_watchPt = sbTarget.WatchAddress(address, size, read, write, sbError); |
| 1295 | + |
| 1296 | + if (!m_watchPt) { |
| 1297 | + const char *type = "write"; |
| 1298 | + if (pArgAccess->GetFound()) |
| 1299 | + type = "access"; |
| 1300 | + else if (pArgRead->GetFound()) |
| 1301 | + type = "read"; |
| 1302 | + |
| 1303 | + SetError(CMIUtilString::Format( |
| 1304 | + MIRSRC(IDS_CMD_ERR_CREATE_WATCHPT), m_cmdData.strMiCmd.c_str(), type, |
| 1305 | + static_cast<uint64_t>(address), static_cast<uint64_t>(size))); |
| 1306 | + return MIstatus::failure; |
| 1307 | + } |
| 1308 | + |
| 1309 | + if (!rSessionInfo.GetStopPtInfo(m_watchPt, m_stopPtInfo)) { |
| 1310 | + SetError(CMIUtilString::Format( |
| 1311 | + MIRSRC(IDS_CMD_ERR_WATCHPT_STOPPT_INFO_CREATE), |
| 1312 | + m_cmdData.strMiCmd.c_str(), static_cast<uint64_t>(m_watchPt.GetID()))); |
| 1313 | + return MIstatus::failure; |
| 1314 | + } |
| 1315 | + |
| 1316 | + m_stopPtInfo.m_bDisp = false; |
| 1317 | + m_stopPtInfo.m_bEnabled = m_watchPt.IsEnabled(); |
| 1318 | + m_stopPtInfo.m_bHaveArgOptionThreadGrp = false; |
| 1319 | + m_stopPtInfo.m_nTimes = m_watchPt.GetHitCount(); |
| 1320 | + m_stopPtInfo.m_watchPtVariable = isVariable; |
| 1321 | + m_stopPtInfo.m_watchPtExpr = expression; |
| 1322 | + m_stopPtInfo.m_watchPtRead = read; |
| 1323 | + m_stopPtInfo.m_watchPtWrite = write; |
| 1324 | + m_stopPtInfo.m_nIgnore = m_watchPt.GetIgnoreCount(); |
| 1325 | + m_stopPtInfo.m_bPending = false; |
| 1326 | + m_stopPtInfo.m_bCondition = m_watchPt.GetCondition() != nullptr; |
| 1327 | + m_stopPtInfo.m_strCondition = |
| 1328 | + m_stopPtInfo.m_bCondition ? m_watchPt.GetCondition() : ""; |
| 1329 | + m_stopPtInfo.m_bBrkPtThreadId = false; |
| 1330 | + |
| 1331 | + if (!rSessionInfo.RecordStopPtInfo(m_stopPtInfo)) { |
| 1332 | + SetError(CMIUtilString::Format( |
| 1333 | + MIRSRC(IDS_CMD_ERR_STOPPT_INFO_SET), m_cmdData.strMiCmd.c_str(), |
| 1334 | + static_cast<uint64_t>(m_stopPtInfo.m_nMiId))); |
| 1335 | + return MIstatus::failure; |
| 1336 | + } |
| 1337 | + |
| 1338 | + return MIstatus::success; |
| 1339 | +} |
| 1340 | + |
| 1341 | +//++ |
| 1342 | +// Details: The invoker requires this function. The command prepares a MI Record |
| 1343 | +// Result for the work carried out in the Execute(). |
| 1344 | +// Type: Overridden. |
| 1345 | +// Args: None. |
| 1346 | +// Return: MIstatus::success - Functional succeeded. |
| 1347 | +// MIstatus::failure - Functional failed. |
| 1348 | +// Throws: None. |
| 1349 | +//-- |
| 1350 | +bool CMICmdCmdBreakWatch::Acknowledge() { |
| 1351 | + assert(m_watchPt.IsValid()); |
| 1352 | + |
| 1353 | + CMICmnMIValueResult miValueResult; |
| 1354 | + CMICmnLLDBDebugSessionInfo::Instance().MIResponseFormWatchPtInfo( |
| 1355 | + m_stopPtInfo, miValueResult); |
| 1356 | + |
| 1357 | + const CMICmnMIResultRecord miRecordResult( |
| 1358 | + m_cmdData.strMiCmdToken, CMICmnMIResultRecord::eResultClass_Done, |
| 1359 | + miValueResult); |
| 1360 | + m_miResultRecord = miRecordResult; |
| 1361 | + |
| 1362 | + return MIstatus::success; |
| 1363 | +} |
| 1364 | + |
| 1365 | +//++ |
| 1366 | +// Details: Required by the CMICmdFactory when registering *this command. The |
| 1367 | +// factory calls this function to create an instance of *this command. |
| 1368 | +// Type: Static method. |
| 1369 | +// Args: None. |
| 1370 | +// Return: CMICmdBase * - Pointer to a new command. |
| 1371 | +// Throws: None. |
| 1372 | +//-- |
| 1373 | +CMICmdBase *CMICmdCmdBreakWatch::CreateSelf() { |
| 1374 | + return new CMICmdCmdBreakWatch(); |
| 1375 | +} |
0 commit comments