Commit 353f3314 by Ben Clayton

Vulkan/Debug: Add Thread and EventListener

EventListener is an interface that is used to listen for thread events. Thread holds the state for a single thread of execution. Frame holds a number of variable scopes for one of a thread's stack frame. Scope is a container for frame variables. Bug: b/145351270 Change-Id: Ic61e17f32cfd6929dbd7b0fce1ffb716301fc73e Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/38897Reviewed-by: 's avatarNicolas Capens <nicolascapens@google.com> Tested-by: 's avatarBen Clayton <bclayton@google.com> Kokoro-Presubmit: kokoro <noreply+kokoro@google.com>
parent 93433f07
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef VK_DEBUG_EVENT_LISTENER_HPP_
#define VK_DEBUG_EVENT_LISTENER_HPP_
namespace vk
{
namespace dbg
{
class Thread;
// EventListener is an interface that is used to listen for thread events.
class EventListener
{
public:
virtual ~EventListener() = default;
// onThreadStarted() is called when a new thread begins execution.
virtual void onThreadStarted(ID<Thread>) {}
// onThreadStepped() is called when a thread performs a single line /
// instruction step.
virtual void onThreadStepped(ID<Thread>) {}
// onLineBreakpointHit() is called when a thread hits a line breakpoint and
// pauses execution.
virtual void onLineBreakpointHit(ID<Thread>) {}
// onFunctionBreakpointHit() is called when a thread hits a function
// breakpoint and pauses execution.
virtual void onFunctionBreakpointHit(ID<Thread>) {}
};
} // namespace dbg
} // namespace vk
#endif // VK_DEBUG_EVENT_LISTENER_HPP_
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "Thread.hpp"
#include "Context.hpp"
#include "EventListener.hpp"
#include "File.hpp"
namespace vk
{
namespace dbg
{
Thread::Thread(ID id, Context* ctx) :
id(id),
broadcast(ctx->broadcast()) {}
void Thread::setName(const std::string& name)
{
std::unique_lock<std::mutex> lock(mutex);
name_ = name;
}
std::string Thread::name() const
{
std::unique_lock<std::mutex> lock(mutex);
return name_;
}
void Thread::update(const Location& location)
{
std::unique_lock<std::mutex> lock(mutex);
frames.back()->location = location;
if(state_ == State::Running)
{
if(location.file->hasBreakpoint(location.line))
{
broadcast->onLineBreakpointHit(id);
state_ = State::Paused;
}
}
switch(state_)
{
case State::Paused:
{
stateCV.wait(lock, [this] { return state_ != State::Paused; });
break;
}
case State::Stepping:
{
if(!pauseAtFrame || pauseAtFrame == frames.back())
{
broadcast->onThreadStepped(id);
state_ = State::Paused;
stateCV.wait(lock, [this] { return state_ != State::Paused; });
pauseAtFrame = 0;
}
break;
}
case State::Running:
break;
}
}
void Thread::enter(Context::Lock& ctxlck, const std::shared_ptr<File>& file, const std::string& function)
{
auto frame = ctxlck.createFrame(file);
auto isFunctionBreakpoint = ctxlck.isFunctionBreakpoint(function);
std::unique_lock<std::mutex> lock(mutex);
frame->function = function;
frames.push_back(frame);
if(isFunctionBreakpoint)
{
broadcast->onFunctionBreakpointHit(id);
state_ = State::Paused;
}
}
void Thread::exit()
{
std::unique_lock<std::mutex> lock(mutex);
frames.pop_back();
}
std::shared_ptr<VariableContainer> Thread::registers() const
{
std::unique_lock<std::mutex> lock(mutex);
return frames.back()->registers->variables;
}
std::shared_ptr<VariableContainer> Thread::locals() const
{
std::unique_lock<std::mutex> lock(mutex);
return frames.back()->locals->variables;
}
std::shared_ptr<VariableContainer> Thread::arguments() const
{
std::unique_lock<std::mutex> lock(mutex);
return frames.back()->arguments->variables;
}
std::shared_ptr<VariableContainer> Thread::hovers() const
{
std::unique_lock<std::mutex> lock(mutex);
return frames.back()->hovers->variables;
}
std::vector<Frame> Thread::stack() const
{
std::unique_lock<std::mutex> lock(mutex);
std::vector<Frame> out;
out.reserve(frames.size());
for(auto frame : frames)
{
out.push_back(*frame);
}
return out;
}
Thread::State Thread::state() const
{
std::unique_lock<std::mutex> lock(mutex);
return state_;
}
void Thread::resume()
{
std::unique_lock<std::mutex> lock(mutex);
state_ = State::Running;
lock.unlock();
stateCV.notify_all();
}
void Thread::pause()
{
std::unique_lock<std::mutex> lock(mutex);
state_ = State::Paused;
}
void Thread::stepIn()
{
std::unique_lock<std::mutex> lock(mutex);
state_ = State::Stepping;
pauseAtFrame.reset();
stateCV.notify_all();
}
void Thread::stepOver()
{
std::unique_lock<std::mutex> lock(mutex);
state_ = State::Stepping;
pauseAtFrame = frames.back();
stateCV.notify_all();
}
void Thread::stepOut()
{
std::unique_lock<std::mutex> lock(mutex);
state_ = State::Stepping;
pauseAtFrame = (frames.size() > 1) ? frames[frames.size() - 1] : nullptr;
stateCV.notify_all();
}
} // namespace dbg
} // namespace vk
\ No newline at end of file
// Copyright 2019 The SwiftShader Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef VK_DEBUG_THREAD_HPP_
#define VK_DEBUG_THREAD_HPP_
#include "Context.hpp"
#include "ID.hpp"
#include "Location.hpp"
#include <condition_variable>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
namespace vk
{
namespace dbg
{
class File;
class VariableContainer;
class EventListener;
// Scope is a container for variables and is used to provide source data for the
// DAP 'Scope' type:
// https://microsoft.github.io/debug-adapter-protocol/specification#Types_Scope
class Scope
{
public:
using ID = dbg::ID<Scope>;
inline Scope(ID id,
const std::shared_ptr<File>& file,
const std::shared_ptr<VariableContainer>& variables);
// The unique identifier of the scope.
const ID id;
// The file this scope is associated with.
const std::shared_ptr<File> file;
// The scope's variables.
const std::shared_ptr<VariableContainer> variables;
};
Scope::Scope(ID id,
const std::shared_ptr<File>& file,
const std::shared_ptr<VariableContainer>& variables) :
id(id),
file(file),
variables(variables) {}
// Frame holds a number of variable scopes for one of a thread's stack frame,
// and is used to provide source data for the DAP 'StackFrame' type:
// https://microsoft.github.io/debug-adapter-protocol/specification#Types_StackFrame
class Frame
{
public:
using ID = dbg::ID<Frame>;
inline Frame(ID id);
// The unique identifier of the stack frame.
const ID id;
// The name of function for this stack frame.
std::string function;
// The current execution location within the stack frame.
Location location;
// The scope for the frame's arguments.
std::shared_ptr<Scope> arguments;
// The scope for the frame's locals.
std::shared_ptr<Scope> locals;
// The scope for the frame's registers.
std::shared_ptr<Scope> registers;
// The scope for variables that should only appear in hover tooltips.
std::shared_ptr<Scope> hovers;
};
Frame::Frame(ID id) :
id(id) {}
// Thread holds the state for a single thread of execution.
class Thread
{
public:
using ID = dbg::ID<Thread>;
// The current execution state.
enum class State
{
Running, // Thread is running.
Stepping, // Thread is currently single line stepping.
Paused // Thread is currently paused.
};
Thread(ID id, Context* ctx);
// setName() sets the name of the thread.
void setName(const std::string&);
// name() returns the name of the thread.
std::string name() const;
// enter() pushes the thread's stack with a new frame created with the given
// file and function.
void enter(Context::Lock& lock, const std::shared_ptr<File>& file, const std::string& function);
// exit() pops the thread's stack frame.
void exit();
// registers() returns the thread's current stack frame's register variables.
std::shared_ptr<VariableContainer> registers() const;
// locals() returns the thread's current stack frame's local variables.
std::shared_ptr<VariableContainer> locals() const;
// arguments() returns the thread's current stack frame's argument variables.
std::shared_ptr<VariableContainer> arguments() const;
// hovers() returns the thread's current stack frame's hover variables.
std::shared_ptr<VariableContainer> hovers() const;
// stack() returns a copy of the thread's current stack frames.
std::vector<Frame> stack() const;
// state() returns the current thread's state.
State state() const;
// resume() resumes execution of the thread by unblocking a call to
// update() and setting the thread's state to State::Running.
void resume();
// pause() suspends execution of the thread by blocking the next call to
// update() and setting the thread's state to State::Paused.
void pause();
// stepIn() temporarily resumes execution of the thread by unblocking a
// call to update(), and setting the thread's state to State::Stepping.
// The next call to update() will suspend execution again.
void stepIn();
// stepOver() temporarily resumes execution of the thread by unblocking a
// call to update(), and setting the thread's state to State::Stepping.
// The next call to update() within the same stack frame will suspend
// execution again.
void stepOver();
// stepOut() temporarily resumes execution of the thread by unblocking a
// call to update(), and setting the thread's state to State::Stepping.
// The next call to update() at the stack frame above the current frame will
// suspend execution again.
void stepOut();
// update() updates the current stack frame's location, and potentially
// blocks until the thread is resumed with one of the methods above.
void update(const Location& location);
// The unique identifier of the thread.
const ID id;
private:
EventListener* const broadcast;
mutable std::mutex mutex;
std::string name_;
std::vector<std::shared_ptr<Frame>> frames;
std::condition_variable stateCV;
State state_ = State::Running;
std::shared_ptr<Frame> pauseAtFrame;
};
} // namespace dbg
} // namespace vk
#endif // VK_DEBUG_THREAD_HPP_
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment