void AMXStackFramePrinter::Print(const AMXStackFrame &frame) { PrintReturnAddress(frame); *stream_ << " in "; AMXDebugSymbol caller = GetCallerSymbol(frame); if (caller) { PrintCallerName(frame, caller); } else { PrintCallerName(frame); } *stream_ << " ("; PrintArgumentList(frame); *stream_ << ")"; if (HaveDebugInfo() && UsesAutomata(frame)) { *stream_ << " "; PrintState(frame); } if (HaveDebugInfo() && frame.return_address() != 0) { *stream_ << " at "; PrintSourceLocation(frame.return_address()); } }
void AMXStackFramePrinter::PrintArgumentValue(const AMXStackFrame &frame, int index) { cell value = GetArgumentValue(frame.amx(), frame.address(), index); char old_fill = stream_->fill('0'); *stream_ << std::hex << "0x" << std::setw(kCellWidthChars) << value << std::dec; stream_->fill(old_fill); }
void AMXStackFramePrinter::PrintCallerName(const AMXStackFrame &frame, const AMXDebugSymbol &caller) { bool is_public = IsPublicFunction(frame.amx(), caller.GetCodeStart()); bool is_main = IsMain(frame.amx(), caller.GetCodeStart()); if (is_public && !is_main) { *stream_ << "public "; } PrintTag(caller); *stream_ << caller.GetName(); }
void AMXStackFramePrinter::PrintReturnAddress(const AMXStackFrame &frame) { if (frame.return_address() == 0) { *stream_ << "????????"; } else { char old_fill = stream_->fill('0'); *stream_ << std::hex << std::setw(kCellWidthChars) << frame.return_address() << std::dec; stream_->fill(old_fill); } }
void AMXStackFramePrinter::PrintCallerName(const AMXStackFrame &frame) { if (IsMain(frame.amx(), frame.caller_address())) { stream_ << "main"; return; } if (debug_info_.IsLoaded()) { AMXDebugSymbol caller = debug_info_.GetExactFunction(frame.caller_address()); if (caller) { if (IsPublicFunction(frame.amx(), caller.GetCodeStart()) && !IsMain(frame.amx(), caller.GetCodeStart())) { stream_ << "public "; } PrintTag(caller); stream_ << caller.GetName(); return; } } const char *name = 0; if (frame.caller_address() != 0) { name = frame.amx().FindPublic(frame.caller_address()); } if (name != 0) { stream_ << "public " << name; } else { stream_ << "??"; } }
void AMXStackFramePrinter::PrintCallerName(const AMXStackFrame &frame) { if (IsMain(frame.amx(), frame.caller_address())) { *stream_ << "main"; } else { const char *name = 0; if (frame.caller_address() != 0) { name = frame.amx().FindPublic(frame.caller_address()); } if (name != 0) { *stream_ << "public " << name; } else { *stream_ << "??"; } } }
void AMXStackFramePrinter::Print(const AMXStackFrame &frame) { PrintReturnAddress(frame); stream_ << " in "; PrintCallerNameAndArguments(frame); if (debug_info_.IsLoaded() && UsesAutomata(frame)) { stream_ << " "; PrintState(frame); } if (debug_info_.IsLoaded() && frame.return_address() != 0) { stream_ << " at "; PrintSourceLocation(frame.return_address()); } }
AMXDebugSymbol AMXStackFramePrinter::GetCallerSymbol( const AMXStackFrame &frame) const { AMXDebugSymbol caller; if (HaveDebugInfo()) { caller = debug_info_->GetExactFunction(frame.caller_address()); } return caller; }
void AMXStackFramePrinter::PrintArgumentValue(const AMXStackFrame &frame, const AMXDebugSymbol &arg, int index) { std::string tag_name = debug_info_.GetTagName(arg.GetTag()); cell value = GetArgumentValue(frame, index); if (arg.IsVariable()) { PrintValue(tag_name, value); return; } stream_ << "@"; PrintAddress(value); if (arg.IsReference()) { if (cell *ptr = GetDataPtr(frame.amx(), value)) { stream_ << " "; PrintValue(tag_name, *ptr); } return; } if (arg.IsArray() || arg.IsArrayRef()) { std::vector<AMXDebugSymbolDim> dims = arg.GetDims(); // Try to filter out non-printable arrays (e.g. non-strings). // This doesn't work 100% of the time, but it's better than nothing. if (dims.size() == 1 && tag_name == "_" && debug_info_.GetTagName(dims[0].GetTag()) == "_") { std::string string; bool packed; GetStringContents(frame.amx(), value, dims[0].GetSize(), string, packed); stream_ << (packed ? " !" : " "); static const std::size_t kMaxString = 80; if (string.length() > kMaxString) { string.replace(kMaxString, string.length() - kMaxString, "..."); } stream_ << "\"" << string << "\""; } } }
void AMXStackFramePrinter::PrintArgumentList(const AMXStackFrame &frame) { AMXStackFrame prev_frame = frame.GetPrevious(); if (prev_frame) { // Although the symbol's code start address points at the state // switch code block, function arguments actually use the real // function address for the code start because in different states // they may be not the same. cell arg_address = frame.caller_address(); if (UsesAutomata(frame)) { arg_address = GetRealFunctionAddress(frame.amx(), frame.caller_address(), frame.return_address()); } std::vector<AMXDebugSymbol> args; int num_actual_args = 0; if (HaveDebugInfo()) { std::remove_copy_if(debug_info_->GetSymbols().begin(), debug_info_->GetSymbols().end(), std::back_inserter(args), std::not1(IsArgumentOf(arg_address))); std::sort(args.begin(), args.end()); num_actual_args = static_cast<int>(args.size()); } else { static const int kMaxRawArgs = 10; num_actual_args = std::min(kMaxRawArgs, GetNumArgs(frame.amx(), prev_frame.address())); } // Print a comma-separated list of arguments and their values. // If debug info is not available argument names are omitted, // so only the values are printed. for (int i = 0; i < num_actual_args; i++) { if (i > 0) { *stream_ << ", "; } if (HaveDebugInfo()) { PrintArgument(prev_frame, args[i], i); } else { PrintArgument(prev_frame, i); } } // If the number of actual arguments passed to the function exceeds // that obtained via debug info the function may take a variable // number of arguments. In this case we don't evaluate them but just // just say that they are present as we can't say anything about // their names and types. int num_var_args = GetNumArgs(frame.amx(), prev_frame.address()) - num_actual_args; if (num_var_args > 0) { if (num_actual_args != 0) { *stream_ << ", "; } PrintVariableArguments(num_var_args); } } }
void AMXStackFramePrinter::PrintArgumentValue(const AMXStackFrame &frame, const AMXDebugSymbol &arg, int index) { std::string tag_name = debug_info_->GetTagName(arg.GetTag()); cell value = GetArgumentValue(frame.amx(), frame.address(), index); if (arg.IsVariable()) { if (tag_name == "bool") { *stream_ << (value ? "true" : "false"); } else if (tag_name == "Float") { *stream_ << std::fixed << std::setprecision(5) << amx_ctof(value); } else { *stream_ << value; } } else { std::vector<AMXDebugSymbolDim> dims = arg.GetDims(); // For arrays/references we just output their AMX address. char old_fill = stream_->fill('0'); *stream_ << "@0x" << std::hex << std::setw(kCellWidthChars) << value << std::dec; stream_->fill(old_fill); if ((arg.IsArray() || arg.IsArrayRef()) && dims.size() == 1 && tag_name == "_" && debug_info_->GetTagName(dims[0].GetTag()) == "_") { std::string string; bool packed; GetStringContents(frame.amx(), value, dims[0].GetSize(), string, packed); *stream_ << (packed ? " !" : " "); static const std::size_t kMaxString = 30; if (string.length() > kMaxString) { string.replace(kMaxString, string.length() - kMaxString, "..."); } *stream_ << "\"" << string << "\""; } } }
void AMXStackFramePrinter::PrintState(const AMXStackFrame &frame) { AMXDebugAutomaton automaton = debug_info_->GetAutomaton( GetStateVarAddress(frame.amx(), frame.caller_address())); if (automaton) { std::vector<cell> states = GetStateIDs(frame.amx(), frame.caller_address(), frame.return_address()); if (!states.empty()) { *stream_ << "<" << automaton.GetName() << ":"; for (std::size_t i = 0; i < states.size(); i++ ) { if (i > 0) { *stream_ << ", "; } AMXDebugState state = debug_info_->GetState(automaton.GetID(), states[i]); if (state) { *stream_ << state.GetName(); } } *stream_ << ">"; } } }
bool AMXStackFramePrinter::UsesAutomata(const AMXStackFrame &frame) const { return GetStateVarAddress(frame.amx(), frame.caller_address()) > 0; }
void AMXStackFramePrinter::PrintReturnAddress(const AMXStackFrame &frame) { PrintAddress(frame.return_address()); }