Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reenable callback on |break| instruction #216

Merged
merged 13 commits into from Oct 2, 2018
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
46 changes: 41 additions & 5 deletions include/sp_vm_api.h
Expand Up @@ -24,7 +24,7 @@

/** SourcePawn Engine API Versions */
#define SOURCEPAWN_ENGINE2_API_VERSION 0xC
#define SOURCEPAWN_API_VERSION 0x020D
#define SOURCEPAWN_API_VERSION 0x020E

namespace SourceMod {
struct IdentityToken_t;
Expand Down Expand Up @@ -293,6 +293,39 @@ namespace SourcePawn
* @param line Pointer to store line number in.
*/
virtual int LookupLine(ucell_t addr, uint32_t *line) =0;

/**
* @brief Given the name of a function and a source file, finds the code pointer
* of the start of the function.
*
* @param function Name of the function to lookup.
* @param file Name of the file containing the function to lookup.
* @param addr Output pointer to store address of function in.
*/
virtual int LookupFunctionAddress(const char* function, const char* file, ucell_t* addr) =0;

/**
* @brief Given a line number and a source file, finds the code pointer of the line.
*
* @param line The line number.
* @param file Name of the file containing the line to lookup.
* @param addr Output pointer to store address of line in.
*/
virtual int LookupLineAddress(const uint32_t line, const char* file, ucell_t* addr) =0;

/**
* @brief Returns the number of source files compiled into this plugin.
*/
virtual size_t NumFiles() =0;

/**
* @brief Returns the full file name and path of the source file
* at the given index.
*
* @param index Index of selected file in the list of source files.
* @return Full file name of source file or NULL if not found.
*/
virtual const char* GetFileName(size_t index) =0;
};

class ICompilation;
Expand Down Expand Up @@ -611,12 +644,15 @@ namespace SourcePawn
virtual bool IsDebugging() =0;

/**
* @brief Deprecated, does nothing.
* @brief Installs a debug break and returns the old one, if any.
* This will fail if the plugin is not debugging.
*
* @param newpfn New function pointer.
* @param oldpfn Pointer to retrieve old function pointer.
*
* @param newpfn Unused.
* @param oldpfn Unused.
* @return Error number.
*/
virtual int SetDebugBreak(void *newpfn, void *oldpfn) =0;
virtual int SetDebugBreak(SPVM_DEBUGBREAK newpfn, SPVM_DEBUGBREAK* oldpfn) =0;

/**
* @brief Deprecated, do not use.
Expand Down
25 changes: 25 additions & 0 deletions include/sp_vm_types.h
Expand Up @@ -55,6 +55,8 @@ typedef uint32_t funcid_t; /**< Function index code */
#define SP_PROF_CALLBACKS (1<<1) /**< Profile callbacks. */
#define SP_PROF_FUNCTIONS (1<<2) /**< Profile functions. */

#define DEBUG_BREAK_INFO_VERSION 0x0001 /**< Version of the sp_debug_break_info_t struct. */

/**
* @brief Error codes for SourcePawn routines.
*/
Expand Down Expand Up @@ -109,6 +111,7 @@ namespace SourcePawn
class IPluginContext;
class IVirtualMachine;
class IProfiler;
class IErrorReport;
};

struct sp_context_s;
Expand Down Expand Up @@ -246,4 +249,26 @@ typedef struct sp_debug_symbol_s
sp_debug_symbol_raw_t *sym; /**< Pointer to original symbol */
} sp_debug_symbol_t;

/**
* @brief Context describing the VM state when the SPVM_DEBUGBREAK
* callback is called.
*/
typedef struct sp_debug_break_info_s
{
uint16_t version; /**< Version of this struct */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How is this version field supposed to be used?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To know whether new fields are available. When adding more info to the struct, like maybe a pointer to the smx in memory to parse the debug symbols manually, allow extensions know which fields they can use.

Probably could get rid of this seperate version field and just increase the whole vm api version if it's changed. Extensions just don't have access to SOURCEPAWN_API_VERSION.

cell_t cip; /**< Current virtual instruction pointer */
cell_t frm; /**< Current virtual frame pointer */
} sp_debug_break_info_t;

/**
* Breaks into a debugger.
* If the exception parameter is not null,
* the function is called with the location of the error.
* Params:
* [0] - plugin context
* [1] - debug info
* [2] - exception
*/
typedef void(*SPVM_DEBUGBREAK)(SourcePawn::IPluginContext*, sp_debug_break_info_t&, const SourcePawn::IErrorReport*);

#endif //_INCLUDE_SOURCEPAWN_VM_TYPES_H
1 change: 1 addition & 0 deletions vm/AMBuilder
Expand Up @@ -53,6 +53,7 @@ library.sources += [
'code-stubs.cpp',
'control-flow.cpp',
'compiled-function.cpp',
'debugging.cpp',
'environment.cpp',
'file-utils.cpp',
'graph-builder.cpp',
Expand Down
2 changes: 1 addition & 1 deletion vm/base-context.cpp
Expand Up @@ -74,7 +74,7 @@ BasePluginContext::IsDebugging()
}

int
BasePluginContext::SetDebugBreak(void* newpfn, void* oldpfn)
BasePluginContext::SetDebugBreak(SPVM_DEBUGBREAK newpfn, SPVM_DEBUGBREAK* oldpfn)
{
return SP_ERROR_ABORTED;
}
Expand Down
2 changes: 1 addition & 1 deletion vm/base-context.h
Expand Up @@ -43,6 +43,7 @@ class BasePluginContext : public SourcePawn::IPluginContext
cell_t BlamePluginError(SourcePawn::IPluginFunction* pf, const char* msg, ...) override;
IFrameIterator* CreateFrameIterator() override;
void DestroyFrameIterator(IFrameIterator* it) override;
int SetDebugBreak(SPVM_DEBUGBREAK newpfn, SPVM_DEBUGBREAK* oldpfn) override;

// Removed functions.
int PushCell(cell_t value) override;
Expand All @@ -56,7 +57,6 @@ class BasePluginContext : public SourcePawn::IPluginContext
SourcePawn::IVirtualMachine* GetVirtualMachine() override;
sp_context_t* GetContext() override;
bool IsDebugging() override;
int SetDebugBreak(void* newpfn, void* oldpfn) override;
SourcePawn::IPluginDebugInfo* GetDebugInfo() override;
int Execute(uint32_t code_addr, cell_t* result) override;
int Execute2(SourcePawn::IPluginFunction* function, const cell_t* params, unsigned int num_params, cell_t* result) override;
Expand Down
62 changes: 62 additions & 0 deletions vm/debugging.cpp
@@ -0,0 +1,62 @@
// vim: set sts=2 ts=8 sw=2 tw=99 et:
//
// Copyright (C) 2016-2018 AlliedModders LLC
//
// This file is part of SourcePawn. SourcePawn is free software: you can
// redistribute it and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation, either version 3 of
// the License, or (at your option) any later version.
//
// You should have received a copy of the GNU General Public License along with
// SourcePawn. If not, see http://www.gnu.org/licenses/.
//
#include "debugging.h"
#include "stack-frames.h"
#include "environment.h"
#include "watchdog_timer.h"
#include <amtl/am-raii.h>

namespace sp {

void InvokeDebugger(PluginContext* ctx, const IErrorReport* report)
{
// Continue normal execution, if this plugin isn't being debugged.
if (!ctx->debugbreak())
return;

if (!ctx->IsDebugging()) {
ctx->ReportErrorNumber(SP_ERROR_NOTDEBUGGING);
return;
}

cell_t cip = 0;

// Find first scripted frame on the stack to get the cip from.
// There might be some native or helper frames beforehand.
{
FrameIterator iter;
for (; !iter.Done(); iter.Next()) {
if (iter.IsScriptedFrame()) {
cip = iter.cip();
break;
}
}
}

// Tell the watchdog to take a break.
// We might stay in the debugger callback for a while,
// so don't let the watchdog hit immediately after
// continueing with execution.
ke::SaveAndSet<bool> disableWatchdog(&Environment::get()->watchdog()->ignore_timeout_, true);

// Fill in the debug info struct.
sp_debug_break_info_t dbginfo;
dbginfo.version = DEBUG_BREAK_INFO_VERSION;
dbginfo.cip = cip;
dbginfo.frm = ctx->frm();

// Call debug callback.
ctx->debugbreak()(ctx, dbginfo, report);
}

} // namespace sp
29 changes: 29 additions & 0 deletions vm/debugging.h
@@ -0,0 +1,29 @@
// vim: set ts=8 sts=2 sw=2 tw=99 et:
//
// This file is part of SourcePawn.
//
// SourcePawn is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// SourcePawn is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with SourcePawn. If not, see <http://www.gnu.org/licenses/>.
//
#ifndef _include_sourcepawn_vm_debugging_h_
#define _include_sourcepawn_vm_debugging_h_

#include "plugin-context.h"

namespace sp {

void InvokeDebugger(PluginContext* ctx, const IErrorReport* report);

} // namespace sp

#endif // _include_sourcepawn_vm_debugging_h_
5 changes: 5 additions & 0 deletions vm/environment.cpp
Expand Up @@ -24,6 +24,7 @@
#endif
#include "interpreter.h"
#include "builtins.h"
#include "debugging.h"
#include <stdarg.h>

using namespace sp;
Expand Down Expand Up @@ -410,6 +411,10 @@ Environment::DispatchReport(const ErrorReport& report)
// For now, we always report exceptions even if they might be handled.
if (debugger_)
debugger_->ReportError(report, iter);

// See if the plugin is being debugged
if (top_)
InvokeDebugger(top_->cx(), &report);
}

void
Expand Down
8 changes: 8 additions & 0 deletions vm/interpreter.cpp
Expand Up @@ -16,6 +16,7 @@
// along with SourcePawn. If not, see <http://www.gnu.org/licenses/>.
//
#include "interpreter.h"
#include "debugging.h"
#include "environment.h"
#include "method-info.h"
#include "plugin-context.h"
Expand Down Expand Up @@ -1032,6 +1033,13 @@ Interpreter::visitSTRADJUST_PRI()
return true;
}

bool
Interpreter::visitBREAK()
{
InvokeDebugger(cx_, nullptr);
return !env_->hasPendingException();
}

bool
Interpreter::visitHALT(cell_t value)
{
Expand Down
1 change: 1 addition & 0 deletions vm/interpreter.h
Expand Up @@ -148,6 +148,7 @@ class Interpreter final : public PcodeVisitor
bool visitTRACKER_PUSH_C(cell_t amount) override;
bool visitTRACKER_POP_SETHEAP() override;
bool visitSTRADJUST_PRI() override;
bool visitBREAK() override;
bool visitHALT(cell_t value) override;
bool visitREBASE(cell_t addr, cell_t iv_size, cell_t data_size) override;

Expand Down
3 changes: 3 additions & 0 deletions vm/jit.cpp
Expand Up @@ -138,6 +138,9 @@ CompilerBase::emit()
emitThrowPathIfNeeded(SP_ERROR_INTEGER_OVERFLOW);
emitThrowPathIfNeeded(SP_ERROR_INVALID_NATIVE);

// Common path for invoking line debugger.
emitDebugBreakHandler();

// This has to come very, very last, since it checks whether return paths
// are used.
emitErrorHandlers();
Expand Down
4 changes: 4 additions & 0 deletions vm/jit.h
Expand Up @@ -72,6 +72,7 @@ class CompilerBase : public PcodeVisitor
virtual void emitThrowPath(int err) = 0;
virtual void emitErrorHandlers() = 0;
virtual void emitOutOfBoundsErrorPath(OutOfBoundsErrorPath* path) = 0;
virtual void emitDebugBreakHandler() = 0;

// Helpers.
static int CompileFromThunk(PluginContext* cx, cell_t pcode_offs, void** addrp, uint8_t* pc);
Expand Down Expand Up @@ -122,6 +123,9 @@ class CompilerBase : public PcodeVisitor
Label report_error_;
Label return_reported_error_;

// Debugging.
Label debug_break_;

ke::Vector<BackwardJump> backward_jumps_;
ke::Vector<CipMapEntry> cip_map_;
};
Expand Down
16 changes: 16 additions & 0 deletions vm/legacy-image.h
Expand Up @@ -54,6 +54,10 @@ class LegacyImage
virtual const char* LookupFile(uint32_t code_offset) = 0;
virtual const char* LookupFunction(uint32_t code_offset) = 0;
virtual bool LookupLine(uint32_t code_offset, uint32_t* line) = 0;
virtual bool LookupFunctionAddress(const char* function, const char* file, ucell_t *addr) = 0;
virtual bool LookupLineAddress(const uint32_t line, const char* file, ucell_t* addr) = 0;
virtual size_t NumFiles() const = 0;
virtual const char* GetFileName(size_t index) const = 0;
};

class EmptyImage : public LegacyImage
Expand Down Expand Up @@ -122,6 +126,18 @@ class EmptyImage : public LegacyImage
bool LookupLine(uint32_t code_offset, uint32_t* line) override {
return false;
}
bool LookupFunctionAddress(const char* function, const char* file, ucell_t* addr) override {
return false;
}
bool LookupLineAddress(const uint32_t line, const char* file, ucell_t* addr) override {
return false;
}
size_t NumFiles() const override {
return 0;
}
const char* GetFileName(size_t index) const override {
return nullptr;
}

private:
size_t heap_size_;
Expand Down
6 changes: 2 additions & 4 deletions vm/pcode-reader.h
Expand Up @@ -87,11 +87,9 @@ class PcodeReader
case OP_NOP:
return true;

// This opcode is used to note where line breaks occur. We don't support
// live debugging, and if we did, we could build this map from the lines
// table. So we don't do any callbacks here.
// This opcode is used to note where line breaks occur.
case OP_BREAK:
return true;
return visitor_->visitBREAK();

case OP_LOAD_PRI:
case OP_LOAD_ALT:
Expand Down
5 changes: 5 additions & 0 deletions vm/pcode-visitor.h
Expand Up @@ -46,6 +46,7 @@ static_assert(sizeof(CaseTableEntry) == sizeof(cell_t) * 2,
class PcodeVisitor
{
public:
virtual bool visitBREAK() = 0;
virtual bool visitLOAD(PawnReg dest, cell_t srcaddr) = 0;
virtual bool visitLOAD_S(PawnReg dest, cell_t srcoffs) = 0;
virtual bool visitLREF_S(PawnReg dest, cell_t srcoffs) = 0;
Expand Down Expand Up @@ -139,6 +140,10 @@ class PcodeVisitor
class IncompletePcodeVisitor : public PcodeVisitor
{
public:
virtual bool visitBREAK() override {
assert(false);
return false;
}
virtual bool visitLOAD(PawnReg dest, cell_t srcaddr) override {
assert(false);
return false;
Expand Down