blob: d1674f2991f8bce42c2e294fb882aa9905adf70a [file] [log] [blame]
// Copyright 2019 Google LLC
//
// 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
//
// https://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 THIRD_PARTY_MLIR_EDGE_IREE_TOOLS_DEBUGGER_DEBUG_APP_H_
#define THIRD_PARTY_MLIR_EDGE_IREE_TOOLS_DEBUGGER_DEBUG_APP_H_
#include <SDL.h>
#include "third_party/absl/strings/string_view.h"
#include "third_party/absl/types/optional.h"
#include "third_party/mlir_edge/iree/base/status.h"
#include "third_party/mlir_edge/iree/vm/debug/debug_client.h"
// NOTE: order matters here, imgui must come first:
#include "third_party/dear_imgui/imgui.h"
// NOTE: must follow imgui.h:
#include "third_party/dear_imgui/examples/imgui_impl_opengl3.h"
#include "third_party/dear_imgui/examples/imgui_impl_sdl.h"
// Workaround for terrible bad SDL/graphics driver leaks.
// If you see these macros being used it means that the code between is not
// really under our control and not a leak we would be able to prevent.
#if defined(__has_feature)
#if __has_feature(address_sanitizer)
#include <sanitizer/lsan_interface.h>
#define IREE_DISABLE_LEAK_CHECKS() __lsan_disable()
#define IREE_ENABLE_LEAK_CHECKS() __lsan_enable()
#else
#define IREE_DISABLE_LEAK_CHECKS()
#define IREE_ENABLE_LEAK_CHECKS()
#endif // __has_feature(address_sanitizer)
#endif // __has_feature
namespace iree {
namespace vm {
namespace debug {
// Debug client app UI.
// Uses a DebugClient to communicate with a remote DebugServer and ImGui to
// display a nifty UI.
//
// See the ImGui site for more info: https://github.com/ocornut/imgui
// The most useful thing is the imgui_demo.cpp file that contains example usage
// of most features.
class DebugApp : private DebugClient::Listener {
public:
struct UserBreakpoint {
RemoteBreakpoint::Type type = RemoteBreakpoint::Type::kBytecodeFunction;
const RemoteBreakpoint* active_breakpoint = nullptr;
bool wants_enabled = true;
bool is_enabling = false;
// TODO(benvanik): reuse BreakpointDef here?
std::string module_name;
std::string function_name;
int function_ordinal = -1;
int bytecode_offset = 0;
std::string native_function;
};
static void PumpMainLoopThunk(void* arg);
DebugApp(SDL_Window* window, SDL_GLContext gl_context,
const char* glsl_version);
~DebugApp();
// Connects to the service at the specified address.
Status Connect(absl::string_view service_address);
// Disconnects from the currently connected service, if any.
Status Disconnect();
// Returns true if the remote service is paused at our request.
bool is_paused() const;
// Pumps the main UI loop once.
// This polls the DebugClient, SDL input, and renders the UI.
// It should be called as frequently as possible to ensure snappy UI updates.
// Returns CancelledError if the app is being closed by the user.
Status PumpMainLoop();
// Defines how NavigationToCodeView methods behave.
enum class NavigationMode {
// The target will be opened in a new document tab.
kNewDocument,
// The target will be opened in the current document tab, replacing the
// current contents.
kCurrentDocument,
// The target will be opened in a document tab that mostly matches (like
// the same function in a module at a different offset), otherwise a new
// document will be opened.
kMatchDocument,
};
// Navigates to a particular function offset based on resolution of the given
// arguments. Navigation may happen asynchronously if targets need to be
// resolved or contents fetched.
Status NavigateToCodeView(absl::string_view module_name, int function_ordinal,
int offset, NavigationMode navigation_mode);
Status NavigateToCodeView(absl::string_view module_name,
absl::string_view function_name, int offset,
NavigationMode navigation_mode);
Status NavigateToCodeView(const RemoteFiberState& fiber_state,
int stack_frame_index,
NavigationMode navigation_mode);
Status NavigateToCodeView(const UserBreakpoint& user_breakpoint,
NavigationMode navigation_mode);
private:
struct CodeViewDocument {
// Document display title (and ID).
std::string title;
// Function (and offset within the function) being displayed.
RemoteFunction* function = nullptr;
int bytecode_offset = 0;
// Set to a bytecode offset to have the document focus there.
absl::optional<int> focus_offset;
// Cached info for bytecode display.
struct {
std::vector<std::string> lines;
} bytecode_info;
};
CodeViewDocument* FindMatchingDocument(absl::string_view module_name,
int function_ordinal);
RemoteFiberState* GetSelectedFiberState() const;
Status RefreshActiveBreakpoints();
bool IsStoppedAtBreakpoint(const UserBreakpoint& user_breakpoint) const;
int FindMatchingUserBreakpointIndex(absl::string_view module_name,
int function_ordinal, int offset);
int FindMatchingUserBreakpointIndex(absl::string_view module_name,
absl::string_view function_name,
int offset);
Status ResumeFromBreakpoint(UserBreakpoint* user_breakpoint);
Status OnContextRegistered(const RemoteContext& context) override;
Status OnContextUnregistered(const RemoteContext& context) override;
Status OnModuleLoaded(const RemoteContext& context,
const RemoteModule& module) override;
Status OnFiberRegistered(const RemoteFiberState& fiber_state) override;
Status OnFiberUnregistered(const RemoteFiberState& fiber_state) override;
Status OnBreakpointHit(const RemoteBreakpoint& breakpoint,
const RemoteFiberState& fiber_state) override;
Status LayoutInitialDockSpace();
Status DrawUI();
Status DrawMainMenu();
Status DrawToolbar();
Status DrawBreakpointListPanel();
StatusOr<bool> DrawBreakpoint(UserBreakpoint* user_breakpoint);
Status DrawAddBreakpointDialogs(
absl::optional<RemoteBreakpoint::Type> add_breakpoint_type);
Status DrawAddBytecodeFunctionBreakpointDialog();
Status DrawAddNativeFunctionBreakpointDialog();
Status DrawModuleListPanel();
Status DrawContext(const RemoteContext& context,
const ImGuiTextFilter& filter);
Status DrawModule(RemoteModule* module, const ImGuiTextFilter& filter);
Status DrawLocalListPanel();
Status DrawLocal(RemoteFiberState* fiber_state, int stack_frame_index,
int local_index, const rpc::BufferViewDefT& local);
Status DrawFiberStateListPanel();
Status DrawFiberState(const RemoteFiberState& fiber_state);
Status DrawCodeViewPanels();
StatusOr<bool> DrawCodeViewDocument(CodeViewDocument* document);
Status PrepareBytecodeCodeView(CodeViewDocument* document);
Status DrawBytecodeCodeView(CodeViewDocument* document);
SDL_Window* window_ = nullptr;
SDL_GLContext gl_context_ = nullptr;
ImGuiID dockspace_id_;
ImGuiID dock_top_id_;
ImGuiID dock_left_id_;
ImGuiID dock_bottom_id_;
ImGuiID dock_bottom_left_id_;
ImGuiID dock_bottom_right_id_;
ImGuiID dock_right_id_;
ImGuiID dock_content_id_;
std::unique_ptr<DebugClient> debug_client_;
std::vector<UserBreakpoint> user_breakpoint_list_;
bool is_paused_ = false;
std::vector<const RemoteBreakpoint*> hit_breakpoints_;
bool is_stepping_ = false;
absl::optional<int> selected_fiber_state_id_;
absl::optional<int> selected_stack_frame_index_;
std::vector<std::unique_ptr<CodeViewDocument>> documents_;
};
} // namespace debug
} // namespace vm
} // namespace iree
#endif // THIRD_PARTY_MLIR_EDGE_IREE_TOOLS_DEBUGGER_DEBUG_APP_H_