Ejemplo n.º 1
0
request_id_t SetVariableCommand::targetThreadId(DebuggerSession* session) {
  ServerObject* obj = session->getServerObject(m_objectId);
  if (obj == nullptr) {
    throw DebuggerCommandException("Invalid variablesReference specified.");
  }

  if (obj->objectType() == ServerObjectType::Scope) {
    ScopeObject* scope = static_cast<ScopeObject*>(obj);
    return scope->m_requestId;
  } else if (obj->objectType() == ServerObjectType::Variable) {
    VariableObject* variable = static_cast<VariableObject*>(obj);
    return variable->m_requestId;
  }

  throw DebuggerCommandException("Unexpected server object type");
}
Ejemplo n.º 2
0
bool SetVariableCommand::setObjectVariable(
  DebuggerSession* session,
  const std::string& name,
  const std::string& value,
  VariableObject* object,
  folly::dynamic* result
) {
  Variant& var = object->m_variable;
  assertx(var.isObject());

  HPHP::String key(name);
  ObjectData* obj = var.getObjectData();
  Variant currentValue = obj->o_get(key, false);
  if (!currentValue.isInitialized()) {
    throw DebuggerCommandException(
      "Failed to set variable: Property not found on object."
    );
  }

  TypedValue* propValue = currentValue.asTypedValue();

  setVariableValue(
    session,
    name,
    value,
    propValue,
    object->m_requestId,
    result
  );
  obj->o_set(key, currentValue);
  return true;
}
Ejemplo n.º 3
0
ScopeObject* DebuggerSession::getScopeObject(unsigned int objectId) {
  auto object = getServerObject(objectId);
  if (object != nullptr) {
    if (object->objectType() != ServerObjectType::Scope) {
      throw DebuggerCommandException(
        "Object with the specified ID is not a scope!"
      );
    }
  }

  return static_cast<ScopeObject*>(object);
}
Ejemplo n.º 4
0
SetVariableCommand::SetVariableCommand(
  Debugger* debugger,
  folly::dynamic message
) : VSCommand(debugger, message),
    m_objectId{0} {

  const folly::dynamic& args = tryGetObject(message, "arguments", s_emptyArgs);
  const int variablesReference = tryGetInt(args, "variablesReference", -1);
  if (variablesReference < 0) {
    throw DebuggerCommandException("Invalid variablesReference specified.");
  }

  m_objectId = variablesReference;
}
Ejemplo n.º 5
0
bool SetExceptionBreakpointsCommand::executeImpl(DebuggerSession* session,
                                                 folly::dynamic* /*responseMsg*/
) {
  folly::dynamic& message = getMessage();
  const folly::dynamic& args = tryGetObject(message, "arguments", s_emptyArgs);

  const auto exceptionOptions =
    tryGetObject(args, "exceptionOptions", s_emptyArgs);
  auto breakMode = tryGetString(exceptionOptions, "breakMode", "");

  // TODO: Named exception breakpoint filters not supported yet.
  if (breakMode == "") {
    const folly::dynamic& filters = args["filters"];
    if (filters.isArray()) {
      for (auto it = filters.begin(); it != filters.end(); it++) {
        const folly::dynamic& filterName = *it;
        if (filterName.isString()) {
          const std::string& filterString = filterName.getString();
          breakMode = filterString;
          break;
        }
      }
    }
  }

  ExceptionBreakMode mode = ExceptionBreakMode::BreakNone;
  if (breakMode == "" || breakMode == "never" || breakMode == "none") {
    mode = ExceptionBreakMode::BreakNone;
  } else if (breakMode == "always" || breakMode == "all") {
    mode = ExceptionBreakMode::BreakAll;
  } else if (breakMode == "unhandled" || breakMode == "uncaught") {
    mode = ExceptionBreakMode::BreakUnhandled;
  } else if (breakMode == "userUnhandled") {
    mode = ExceptionBreakMode::BreakUserUnhandled;
  } else {
    throw DebuggerCommandException(
      "Invalid exception break mode specified."
    );
  }

  BreakpointManager* bpMgr = session->getBreakpointManager();
  bpMgr->setExceptionBreakMode(mode);

  // Completion of this command does not resume the target.
  return false;
}
Ejemplo n.º 6
0
bool SetVariableCommand::getBooleanValue(const std::string& str) {
  // Trim leading and trailing whitespace.
  std::string trimmed = trimString(str);

  // Boolean values in PHP are not case sensitive.
  for (char& c : trimmed) {
    c = std::toupper(c);
  }

  if (trimmed == "TRUE") {
    return true;
  } else if (trimmed == "FALSE") {
    return false;
  }

  throw DebuggerCommandException(
    "The specified value was not a valid boolean value."
  );
}
Ejemplo n.º 7
0
bool SetVariableCommand::setArrayVariable(
  DebuggerSession* session,
  const std::string& name,
  const std::string& value,
  VariableObject* array,
  folly::dynamic* result
) {
  VMRegAnchor regAnchor;

  Variant& var = array->m_variable;
  assertx(var.isArray());

  Array arr = var.toArray();
  for (ArrayIter iter(arr); iter; ++iter) {
    std::string indexName = iter.first().toString().toCppString();
    if (indexName == name) {
      TypedValue* arrayValue = iter.second().asTypedValue();
      setVariableValue(
        session,
        name,
        value,
        arrayValue,
        array->m_requestId,
        result
      );

      auto keyVariant = iter.first();
      if (keyVariant.isString()) {
        HPHP::String key = keyVariant.toString();
        arr->set(key, tvToInitCell(*arrayValue), false);
      } else if (keyVariant.isInteger()) {
        int64_t key = keyVariant.toInt64();
        arr->set(key, tvToInitCell(*arrayValue), false);
      } else {
        throw DebuggerCommandException("Unsupported array key type.");
      }
      return true;
    }
  }

  return false;
}
Ejemplo n.º 8
0
bool SetVariableCommand::executeImpl(
  DebuggerSession* session,
  folly::dynamic* responseMsg
) {
  folly::dynamic body = folly::dynamic::object;
  folly::dynamic variables = folly::dynamic::array;
  auto& args = tryGetObject(getMessage(), "arguments", s_emptyArgs);

  ServerObject* obj = session->getServerObject(m_objectId);
  if (obj == nullptr) {
    throw DebuggerCommandException("Invalid variablesReference specified.");
  }

  const std::string& suppliedName = tryGetString(args, "name", "");
  if (suppliedName.empty()) {
    throw DebuggerCommandException("Invalid variable name specified.");
  }

  // Remove any prefixes that we would have added to the variable name
  // before presenting it to the front-end.
  std::string name = removeVariableNamePrefix(suppliedName);
  bool success = false;
  const std::string& strValue = tryGetString(args, "value", "");

  try {
    if (obj->objectType() == ServerObjectType::Scope) {
      ScopeObject* scope = static_cast<ScopeObject*>(obj);

      switch (scope->m_scopeType) {
        case ScopeType::Locals:
          success = setLocalVariable(
            session,
            name,
            strValue,
            scope,
            &body
          );
          break;

        case ScopeType::ServerConstants:
          success = setConstant(
            session,
            name,
            strValue,
            scope,
            &body
          );
          break;

        // Superglobals and core constants are provided by the current execution
        // context, rather than trying to overwrite them here, defer to the PHP
        // console, which will let the runtime enforce whatever policies are
        // appropriate.
        case ScopeType::Superglobals:
          m_debugger->sendUserMessage(
            "Could not directly set value of superglobal variable, you may "
              "be able to set this value by running a Hack/PHP command in the "
              " console.",
            DebugTransport::OutputLevelError
          );
          break;

        default:
          assertx(false);
      }
    } else if (obj->objectType() == ServerObjectType::Variable) {
      VariableObject* variable = static_cast<VariableObject*>(obj);
      Variant& variant = variable->m_variable;
      if (variant.isArray()) {
        success = setArrayVariable(session, name, strValue, variable, &body);
      } else if (variant.isObject()) {
        success = setObjectVariable(session, name, strValue, variable, &body);
      } else {
        throw DebuggerCommandException(
          "Failed to set variable: Unexpected variable type."
        );
      }
    }
  } catch (DebuggerCommandException e) {
    m_debugger->sendUserMessage(
      e.what(),
      DebugTransport::OutputLevelError
    );
    throw e;
  }

  if (!success) {
    throw DebuggerCommandException(
      "Failed to set variable."
    );
  }

  (*responseMsg)["body"] = body;
  return false;
}
Ejemplo n.º 9
0
void SetVariableCommand::setVariableValue(
  DebuggerSession* session,
  const std::string& name,
  const std::string& value,
  TypedValue* typedVariable,
  request_id_t requestId,
  folly::dynamic* result
) {
  switch (typedVariable->m_type) {
    case KindOfBoolean: {
        bool boolVal = getBooleanValue(value);
        typedVariable->m_data.num = boolVal ? 1 : 0;
      }
      break;

    case KindOfInt64:
      try {
        typedVariable->m_data.num = std::stoi(value, nullptr, 0);
      } catch (std::exception e) {
        throw DebuggerCommandException("Invalid value specified.");
      }
      break;

    case KindOfDouble:
      try {
        typedVariable->m_data.dbl = std::stod(value);
      } catch (std::exception e) {
        throw DebuggerCommandException("Invalid value specified.");
      }
      break;

    case KindOfPersistentString:
    case KindOfString: {
      const auto newSd = StringData::Make(
        value.c_str(),
        CopyString
      );

      if (typedVariable->m_type == KindOfString &&
          typedVariable->m_data.pstr != nullptr) {

        typedVariable->m_data.pstr->decRefCount();
      }

      typedVariable->m_data.pstr = newSd;
      typedVariable->m_type = KindOfString;
      break;
    }

    case KindOfUninit:
    case KindOfNull:
      // In the case of uninit and null, we don't even know how to interpret
      // the value from the client, because the object has no known type.
      // Fallthrough.
    case KindOfResource:
    case KindOfPersistentVec:
    case KindOfVec:
    case KindOfPersistentArray:
    case KindOfArray:
    case KindOfPersistentDict:
    case KindOfDict:
    case KindOfPersistentKeyset:
    case KindOfKeyset:
    case KindOfRef:
    case KindOfObject:
      // For complex types, we need to run PHP code to create a new object
      // or determine what reference to assign, making direct assignment via
      // reflection impractical, so we defer to the console REPL, which will use
      // an evaluation command to run PHP.
      // NOTE: It would be nice in the future to just run an eval command here
      // on the user's behalf. At the moment, if we are setting a child prop
      // on an object or array, we don't know the fully qualified name string
      // to pass to PHP though, since we only have a reference to the container.
      throw DebuggerCommandException(
        "Failed to set object value. Please use the console to set the value "
        "of complex objects."
      );

    default:
      throw DebuggerCommandException("Unexpected variable type");
  }

  Variant variable = tvAsVariant(typedVariable);
  *result = VariablesCommand::serializeVariable(
              session,
              requestId,
              name,
              variable
            );
}
Ejemplo n.º 10
0
bool RunToLocationCommand::executeImpl(DebuggerSession* session,
                                       folly::dynamic* /*responseMsg*/
) {
  folly::dynamic& message = getMessage();
  const folly::dynamic& args = tryGetObject(message, "arguments", s_emptyArgs);
  const folly::dynamic& source = tryGetObject(args, "source", s_emptyArgs);
  const std::string& filePath = tryGetString(source, "path", "");

  if (filePath.empty()) {
    throw DebuggerCommandException(
      "Run to location source path not specified."
    );
  }

  const ClientPreferences& prefs = m_debugger->getClientPreferences();
  const std::string& path = File::TranslatePath(String(filePath)).toCppString();
  BreakpointManager* bpMgr = session->getBreakpointManager();

  int line = tryGetInt(args, "line", -1);
  if (!prefs.linesStartAt1) {
    // If client lines start at 0, make them 1 based.
    line++;
  }

  if (line <= 0) {
    throw DebuggerCommandException(
      "Invalid continue to line specified."
    );
  }

  // See if there's already a breakpoint at this file + line.
  const auto bpIds = bpMgr->getBreakpointIdsByFile(path);
  for (auto it = bpIds.begin(); it != bpIds.end(); it++) {
    Breakpoint* bp = bpMgr->getBreakpointById(*it);
    if (bp->m_line == line) {
      // There's already a breakpoint installed at the run to location.
      // Just resume the request thread and let it hit.
      return true;
    }
  }

  // Find a compilation unit to place a temp bp in.
  HPHP::String unitPath(path.c_str());
  const auto compilationUnit = lookupUnit(unitPath.get(), "", nullptr);
  if (compilationUnit == nullptr) {
    throw DebuggerCommandException(
      "Could not find a loaded compilation unit to run to location in!"
    );
  }

  std::pair<int, int> runLocation =
    m_debugger->calibrateBreakpointLineInUnit(compilationUnit, line);

  if (runLocation.first < 0) {
    throw DebuggerCommandException(
      "Could not find a suitable location in the compilation unit to run to!"
    );
  }

  if (!phpAddBreakPointLine(compilationUnit, runLocation.first)) {
    throw DebuggerCommandException(
      "Failed to install temporary breakpoint at location."
    );
  }

  RequestInfo* ri = m_debugger->getRequestInfo();
  ri->m_runToLocationInfo.path = path;
  ri->m_runToLocationInfo.line = line;

  // Tell the user where we're running to. Resolving the file path and
  // calibrating the source line could have modified things a bit.
  std::string userMsg = "Resuming request ";
  userMsg += std::to_string(targetThreadId(session));
  userMsg += " and running to resolved location ";
  userMsg += path;
  userMsg += ":";
  userMsg += std::to_string(runLocation.second);
  m_debugger->sendUserMessage(userMsg.c_str(), DebugTransport::OutputLevelInfo);

  // Resume only this request thread.
  return true;
}
Ejemplo n.º 11
0
bool SetBreakpointsCommand::executeImpl(
  DebuggerSession* session,
  folly::dynamic* responseMsg
) {
  folly::dynamic& message = getMessage();
  const folly::dynamic& args = tryGetObject(message, "arguments", s_emptyArgs);
  const folly::dynamic& source = tryGetObject(args, "source", s_emptyArgs);

  // SourceReference is not supported.
  int sourceRef = tryGetInt(source, "sourceReference", INT_MIN);
  if (sourceRef != INT_MIN) {
    throw DebuggerCommandException("SourceReference breakpoint not supported.");
  }

  const std::string& filePath = tryGetString(source, "path", "");
  if (filePath.empty()) {
    throw DebuggerCommandException(
      "Breakpoint source path not specified."
    );
  }

  const std::string& path =
    StatCache::realpath(
      File::TranslatePath(String(filePath)).toCppString().c_str());

  BreakpointManager* bpMgr = session->getBreakpointManager();

  // Make a map of line -> breakpoint for all breakpoints in this file before
  // this set breakpoints operation.
  std::unordered_map<int, Breakpoint*> oldBpLines;
  const auto oldBpIds = bpMgr->getBreakpointIdsByFile(path);
  for (auto it = oldBpIds.begin(); it != oldBpIds.end(); it++) {
    Breakpoint* bp = bpMgr->getBreakpointById(*it);
    std::pair<int, Breakpoint*> pair;
    pair.first = bp->m_line;
    pair.second = bp;
    oldBpLines.emplace(pair);
  }

  const ClientPreferences& prefs = m_debugger->getClientPreferences();
  const std::string empty = "";

  (*responseMsg)["body"] = folly::dynamic::object;
  (*responseMsg)["body"]["breakpoints"] = folly::dynamic::array();
  auto& responseBps = (*responseMsg)["body"]["breakpoints"];

  try {
    const folly::dynamic& sourceBps = args["breakpoints"];
    if (sourceBps.isArray()) {
      for (auto it = sourceBps.begin(); it != sourceBps.end(); it++) {
        const folly::dynamic& sourceBp = *it;
        if (sourceBp.isObject()) {
          int line = tryGetInt(sourceBp, "line", -1);
          if (!prefs.linesStartAt1) {
            // If client lines start at 0, make them 1 based.
            line++;
          }

          if (line <= 0) {
            throw DebuggerCommandException(
              "Breakpoint has invalid line number."
            );
          }

          int column = tryGetInt(sourceBp, "column", 0);
          if (!prefs.columnsStartAt1) {
            // If client cols start at 0, make them 1 based.
            column++;
          }

          if (column < 0) {
            column = 0;
          }

          const std::string& condition =
            tryGetString(sourceBp, "condition", empty);
          const std::string& hitCondition =
            tryGetString(sourceBp, "hitCondition", empty);

          auto bpIt = oldBpLines.find(line);
          if (bpIt == oldBpLines.end()) {
            // Create a new breakpoint.
            int newBpId = bpMgr->addBreakpoint(
              line,
              column,
              path,
              condition,
              hitCondition
            );

            m_debugger->onBreakpointAdded(newBpId);

            folly::dynamic newBreakpointInfo = folly::dynamic::object;
            newBreakpointInfo["id"] = newBpId;
            newBreakpointInfo["verified"] = false;
            responseBps.push_back(newBreakpointInfo);
          } else {
            Breakpoint* bp = bpIt->second;
            // Update breakpoint conditions in case they've changed.
            bp->updateConditions(condition, hitCondition);

            bool verified = bpMgr->isBreakpointResolved(bp->m_id);
            int responseLine = line;
            if (verified) {
              responseLine = bp->m_resolvedLocation.m_startLine;
              if (!prefs.linesStartAt1) {
                responseLine--;
              }
            }

            folly::dynamic bpInfo = folly::dynamic::object;
            bpInfo["id"] = bp->m_id;
            bpInfo["line"] = responseLine;
            bpInfo["verified"] = verified;
            responseBps.push_back(bpInfo);

            // Remove this bp from oldBpLines so that after processing this
            // loop, oldBpLines contains any breakpoints that did not match
            // a bp in the request from the client.
            oldBpLines.erase(bpIt);
          }
        }
      }

      // Remove any breakpoints that should no longer exist in this file.
      for (auto it = oldBpLines.begin(); it != oldBpLines.end(); it++) {
        const Breakpoint* bp = it->second;
        bpMgr->removeBreakpoint(bp->m_id);
      }
    }
  } catch (std::out_of_range e) {
  }

  // Completion of this command does not resume the target.
  return false;
}