/* +----------------------------------------------------------------------+ | HipHop for PHP | +----------------------------------------------------------------------+ | Copyright (c) 2010-2013 Facebook, Inc. (http://www.facebook.com) | +----------------------------------------------------------------------+ | This source file is subject to version 3.01 of the PHP license, | | that is bundled with this package in the file LICENSE, and is | | available through the world-wide-web at the following url: | | http://www.php.net/license/3_01.txt | | If you did not receive a copy of the PHP license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@php.net so we can mail you a copy immediately. | +----------------------------------------------------------------------+ */ #include "hphp/runtime/debugger/cmd/cmd_instrument.h" #include "hphp/runtime/vm/instrumentation.h" #include "hphp/runtime/ext/ext_file.h" namespace HPHP { namespace Eval { /////////////////////////////////////////////////////////////////////////////// TRACE_SET_MOD(debugger); void CmdInstrument::sendImpl(DebuggerThriftBuffer &thrift) { DebuggerCommand::sendImpl(thrift); thrift.write(m_type); thrift.write(m_enabled); assert(m_instPoints); InstPointInfo::SendImpl(*m_instPoints, thrift); } void CmdInstrument::recvImpl(DebuggerThriftBuffer &thrift) { DebuggerCommand::recvImpl(thrift); thrift.read(m_type); thrift.read(m_enabled); InstPointInfo::RecvImpl(m_ips, thrift); } void CmdInstrument::help(DebuggerClient &client) { client.helpTitle("Instrument Command"); // TODO: more functionalities client.helpCmds("inst here [desc]", "inject to here", "inst () [desc]", "inject to the entry point of ", "inst [l]ist", "list injections", "inst [c]lear", "clear all injections", nullptr); client.helpBody( "Use this command to instrument the program" ); } void CmdInstrument::onClientImpl(DebuggerClient &client) { if (DebuggerCommand::displayedHelp(client)) return; if (client.argCount() == 1) { if (client.argValue(1) == "list" || client.argValue(1) == "l") { listInst(client); return; } if (client.argValue(1) == "clear" || client.argValue(1) == "c") { clearInst(client); return; } } if (client.argCount() < 2 || client.argValue(1) == "help") { help(client); return; } std::string loc = client.argValue(1); std::string file = client.argValue(2); std::string desc; if (client.argCount() >= 3) { desc = client.argValue(3); } Variant code = f_file_get_contents(file.c_str()); if (code.isNull()) { client.error("Unable to read from file %s", file.c_str()); return; } m_instPoints = client.getInstPoints(); if (loc == "here") { InstPointInfoPtr ipi(new InstPointInfo()); ipi->setLocHere(); ipi->m_code = (std::string) code.toString(); ipi->m_desc = desc; m_instPoints->push_back(ipi); } else if (loc.rfind("()") == loc.size() - 2){ InstPointInfoPtr ipi(new InstPointInfo()); ipi->setLocFuncEntry(loc.substr(0, loc.size() - 2)); ipi->m_code = (std::string) code.toString(); ipi->m_desc = desc; m_instPoints->push_back(ipi); } else { client.error("Not implemented\n"); return; } m_type = ActionWrite; CmdInstrumentPtr instCmdPtr = client.xend(this); if (!instCmdPtr->m_enabled) { client.error("Instrumentation is not enabled on the server"); } client.setInstPoints(instCmdPtr->m_ips); CmdInstrument::PrintInstPoints(client); } static const StaticString s_valid("valid"); static const StaticString s_desc("desc"); static const StaticString s_type("type"); static const StaticString s_file_line("file_line"); static const StaticString s_file("file"); static const StaticString s_line("line"); static const StaticString s_func_entry("func_entry"); static const StaticString s_func("func"); void CmdInstrument::setClientOutput(DebuggerClient &client) { // Output all instrumentation point info client.setOutputType(DebuggerClient::OTValues); Array values; InstPointInfoPtrVec* ips = client.getInstPoints(); for (unsigned int i = 0; i < ips->size(); i++) { InstPointInfoPtr ipi = (*ips)[i]; Array instpoint; instpoint.set(s_valid, ipi->m_valid); instpoint.set(s_desc, ipi->m_desc); if (ipi->m_locType == InstPointInfo::LocFileLine) { instpoint.set(s_type, s_file_line); instpoint.set(s_file, ipi->m_file); instpoint.set(s_line, ipi->m_line); } else if (ipi->m_locType == InstPointInfo::LocFuncEntry) { instpoint.set(s_type, s_func_entry); instpoint.set(s_func, ipi->m_func); } values.append(instpoint); } client.setOTValues(values); } bool CmdInstrument::onServer(DebuggerProxy &proxy) { m_instPoints = &m_ips; m_enabled = true; if (m_type == ActionRead) { readFromTable(proxy); } else if (m_type == ActionWrite) { validateAndWriteToTable(proxy); } return proxy.sendToClient(this); } void CmdInstrument::readFromTable(DebuggerProxy &proxy) { proxy.readInjTablesFromThread(); m_ips.clear(); if (!proxy.getInjTables()) { // nothing there return; } // Bytecode address InjectionTableInt64* tablePC = proxy.getInjTables()->getInt64Table(InstHookTypeBCPC); if (tablePC) { for (InjectionTableInt64::const_iterator it = tablePC->begin(); it != tablePC->end(); ++it) { const Injection* inj = it->second; InstPointInfoPtr ipi(new InstPointInfo()); ipi->m_valid = true; if (inj->m_desc) { ipi->m_desc = inj->m_desc->data(); } ipi->m_locType = InstPointInfo::LocFileLine; // TODO use pc to figure out m_file and m_line // uchar* pc = (uchar*)it->first; m_ips.push_back(ipi); } } InjectionTableSD* tableFEntry = proxy.getInjTables()->getSDTable(InstHookTypeFuncEntry); if (tableFEntry) { for (InjectionTableSD::const_iterator it = tableFEntry->begin(); it != tableFEntry->end(); ++it) { const Injection* inj = it->second; InstPointInfoPtr ipi(new InstPointInfo()); ipi->m_valid = true; if (inj->m_desc) { ipi->m_desc = inj->m_desc->data(); } ipi->m_func = it->first->data(); ipi->m_locType = InstPointInfo::LocFuncEntry; m_ips.push_back(ipi); } } } void CmdInstrument::validateAndWriteToTable(DebuggerProxy &proxy) { if (!proxy.getInjTables()) { proxy.setInjTables(new InjectionTables()); } InjectionTableInt64* tablePC = nullptr; InjectionTableSD* tableFEntry = nullptr; for (int i = 0; i < (int)m_ips.size(); i++) { InstPointInfoPtr ipi = m_ips[i]; const Injection* inj = InjectionCache::GetInjection(ipi->m_code, ipi->m_desc); if (!inj) { // error in the code continue; } if (ipi->m_locType == InstPointInfo::LocHere || ipi->m_locType == InstPointInfo::LocFileLine) { // bytecode address const uchar *pc = ipi->lookupPC(); if (pc == nullptr) { continue; } if (tablePC == nullptr) { tablePC = new InjectionTableInt64(); } ipi->m_valid = true; (*tablePC)[(int64_t)pc] = inj; } if (ipi->m_locType == InstPointInfo::LocFuncEntry) { StackStringData sd(ipi->m_func.c_str(), ipi->m_func.size(), AttachLiteral); const StringData* sdCache = InjectionCache::GetStringData(&sd); if (tableFEntry == nullptr) { tableFEntry = new InjectionTableSD(); } ipi->m_valid = true; (*tableFEntry)[sdCache] = inj; } } proxy.getInjTables()->setInt64Table(InstHookTypeBCPC, tablePC); proxy.getInjTables()->setSDTable(InstHookTypeFuncEntry, tableFEntry); proxy.writeInjTablesToThread(); } void CmdInstrument::listInst(DebuggerClient &client) { m_type = ActionRead; m_instPoints = client.getInstPoints(); CmdInstrumentPtr instCmdPtr = client.xend(this); client.setInstPoints(instCmdPtr->m_ips); PrintInstPoints(client); } void CmdInstrument::clearInst(DebuggerClient &client) { m_type = ActionWrite; m_instPoints = client.getInstPoints(); m_instPoints->clear(); CmdInstrumentPtr instCmdPtr = client.xend(this); client.setInstPoints(instCmdPtr->m_ips); PrintInstPoints(client); } void CmdInstrument::PrintInstPoints(DebuggerClient &client) { InstPointInfoPtrVec* ips = client.getInstPoints(); int size = ips->size(); client.print("%d instrumentation points", size); for (int i = 0; i < size; i++) { InstPointInfoPtr ipi = (*ips)[i]; if (ipi->m_locType == InstPointInfo::LocFileLine) { client.print(" %d\t%s\t%s\tfile:\t%s:%d", i, ipi->m_valid ? "valid" : "invalid", ipi->m_desc.c_str(), ipi->m_file.c_str(), ipi->m_line); } else if (ipi->m_locType == InstPointInfo::LocFuncEntry) { client.print(" %d\t%s\t%s\tfunc entry:\t%s", i, ipi->m_valid ? "valid" : "invalid", ipi->m_desc.c_str(), ipi->m_func.c_str()); } } } /////////////////////////////////////////////////////////////////////////////// }}