Repository: angr/angr-management (language: python)

Issue:
Add re-compilation feature to decompilation view
To achieve decompilation of a binary closer, or possibly identical, to original source and to discover subtle bugs in re-implementation, I've automated a simple workflow of adjusting source + compiling + binary diffing. I think this could make for a useful angr-management feature.

A new option to the pseudocode view would be added that allows the user to enable re-compilation and specify compiler options. Whenever a tweak is made that prompts pseudocode regeneration, it is automatically recompiled and a diff against the original is displayed.

What my script displays: ![Screenshot from 2022-08-06 16-43-13](https://user-images.githubusercontent.com/8210/183269824-69d4d601-903a-4b93-ad73-84be84ea7942.png)




Discussion / hints:

Fix patch:
diff --git a/angrmanagement/daemon/client.py b/angrmanagement/daemon/client.py
index 8d089f840..be322cef6 100644
--- a/angrmanagement/daemon/client.py
+++ b/angrmanagement/daemon/client.py
@@ -23,7 +23,7 @@ class ClientService(rpyc.Service):
 
     @property
     def instance(self):
-        return GlobalInfo.main_window.workspace.instance
+        return GlobalInfo.main_window.workspace.main_instance
 
     @property
     def workspace(self):
diff --git a/angrmanagement/data/analysis_options.py b/angrmanagement/data/analysis_options.py
index b3b3c2524..b93bbc51e 100644
--- a/angrmanagement/data/analysis_options.py
+++ b/angrmanagement/data/analysis_options.py
@@ -32,8 +32,8 @@ class AnalysesConfiguration:
     Configuration for a sequence of analyses.
     """
 
-    def __init__(self, analyses: Sequence['AnalysisConfiguration'], workspace: 'Workspace'):
-        self.workspace: 'Workspace' = workspace
+    def __init__(self, analyses: Sequence['AnalysisConfiguration'], instance):
+        self.instance = instance
         self.analyses: Sequence['AnalysisConfiguration'] = analyses
 
     def __len__(self):
@@ -59,9 +59,9 @@ class AnalysisConfiguration:
     Configuration for an analysis.
     """
 
-    def __init__(self, workspace: 'Workspace'):
-        self.workspace: 'Workspace' = workspace
-        self.project: angr.Project = workspace.instance.project.am_obj
+    def __init__(self, instance):
+        self.instance = instance
+        self.project: angr.Project = self.instance.project.am_obj
         self.enabled: bool = False
         self.name: str = ''
         self.display_name: str = ''
@@ -133,8 +133,8 @@ class CFGAnalysisConfiguration(AnalysisConfiguration):
     Configuration for CFGFast analysis.
     """
 
-    def __init__(self, workspace: 'Workspace'):
-        super().__init__(workspace)
+    def __init__(self, instance):
+        super().__init__(instance)
         self.name = 'cfg'
         self.display_name = 'Control-Flow Graph Recovery'
         self.description = extract_first_paragraph_from_docstring(self.project.analyses.CFGFast.__doc__)
@@ -152,8 +152,8 @@ class FlirtAnalysisConfiguration(AnalysisConfiguration):
     Configuration for Flirt analysis.
     """
 
-    def __init__(self, workspace: 'Workspace'):
-        super().__init__(workspace)
+    def __init__(self, instance):
+        super().__init__(instance)
         self.name = 'flirt'
         self.display_name = 'Signature Matching'
         self.description = self.project.analyses.Flirt.__doc__.strip()
@@ -165,8 +165,8 @@ class VariableRecoveryConfiguration(AnalysisConfiguration):
     Configuration for VariableRecovery analysis.
     """
 
-    def __init__(self, workspace: 'Workspace'):
-        super().__init__(workspace)
+    def __init__(self, instance):
+        super().__init__(instance)
         self.name = 'varec'
         self.display_name = 'Variable Recovery'
         self.description = extract_first_paragraph_from_docstring(self.project.analyses.VariableRecovery.__doc__)
diff --git a/angrmanagement/data/instance.py b/angrmanagement/data/instance.py
index 8b42a0a88..5723bc7ba 100644
--- a/angrmanagement/data/instance.py
+++ b/angrmanagement/data/instance.py
@@ -9,7 +9,14 @@
 from angr.block import Block
 from angr.knowledge_base import KnowledgeBase
 from angr.analyses.disassembly import Instruction
-
+from angr.knowledge_plugins import Function
+from angr.misc.testing import is_testing
+from cle import SymbolType
+
+from .analysis_options import AnalysesConfiguration, CFGAnalysisConfiguration, FlirtAnalysisConfiguration, \
+    VariableRecoveryConfiguration
+from .jobs import VariableRecoveryJob, PrototypeFindingJob, CodeTaggingJob, FlirtSignatureRecognitionJob, \
+    CFGGenerationJob
 from .object_container import ObjectContainer
 from .log import LogRecord, LogDumpHandler
 from ..logic import GlobalInfo
@@ -17,11 +24,16 @@
 from ..logic.debugger import DebuggerListManager, DebuggerManager
 from ..logic.debugger.simgr import SimulationDebugger
 from ..data.trace import Trace
-from ..data.breakpoint import BreakpointManager
+from ..data.breakpoint import BreakpointManager, BreakpointType, Breakpoint
+from ..ui.dialogs import AnalysisOptionsDialog
+from ..ui.views import DisassemblyView
 
 if TYPE_CHECKING:
     from ..ui.workspace import Workspace
 
+_l = logging.getLogger(__name__)
+
+
 class Instance:
     """
     An object to give access to normal angr project objects like a Project, CFG, and other analyses.
@@ -39,6 +51,8 @@ def __init__(self):
 
         self._live = False
         self.workspace: Optional['Workspace'] = None
+        self.variable_recovery_job: Optional[VariableRecoveryJob] = None
+        self._analysis_configuration = None
 
         self.jobs = []
         self._jobs_queue = Queue()
@@ -174,7 +188,7 @@ def initialize(self, initialized=False, **kwargs):  # pylint:disable=unused-argu
             if self.pseudocode_variable_kb is None:
                 self.initialize_pseudocode_variable_kb()
 
-            gui_thread_schedule_async(self.workspace.run_analysis)
+            gui_thread_schedule_async(self.run_analysis)
 
         self.workspace.plugins.handle_project_initialization()
 
@@ -230,6 +244,98 @@ def append_code_to_console(self, hook_code_string):
     def delete_hook(self, addr):
         self.project.unhook(addr)
 
+    def add_breakpoint(self, obj: Union[str, int], type_: Optional[str] = None, size: Optional[int] = None):
+        """
+        Convenience function to add a breakpoint.
+
+        Examples:
+        - `workspace.add_breakpoint(0x1234)` sets an execution breakpoint on address 0x1234
+        - `workspace.add_breakpoint('main')` sets an execution breakpoint on `main` function
+        - `workspace.add_breakpoint('global_value')` sets a write breakpoint on `global_value`
+        - `workspace.add_breakpoint('global_value', 'read', 1)` sets a 1-byte read breakpoint on `global_value`
+        """
+        if type(obj) is int:
+            addr = obj
+        elif type(obj) is str:
+            sym = self.project.loader.find_symbol(obj)
+            if sym is None:
+                _l.error("Couldn't resolve '%s'", obj)
+                return
+            addr = sym.rebased_addr
+            if not size:
+                size = sym.size
+            if not type_:
+                if sym.type == SymbolType.TYPE_FUNCTION:
+                    type_ = 'execute'
+                else:
+                    type_ = 'write'
+        elif type(obj) is Function:
+            addr = obj.addr
+            if not type_:
+                type_ = 'execute'
+        else:
+            _l.error('Unexpected target object type. Expected int | str | Function')
+            return
+
+        if not size:
+            size = 1
+
+        bp_type_map = {
+            None: BreakpointType.Execute,
+            'execute': BreakpointType.Execute,
+            'write': BreakpointType.Write,
+            'read': BreakpointType.Read,
+        }
+        if type_ not in bp_type_map:
+            _l.error("Unknown breakpoint type '%s'. Expected %s",
+                     type_, ' | '.join(bp_type_map.keys()))
+            return
+
+        bp = Breakpoint(bp_type_map[type_], addr, size)
+        self.breakpoint_mgr.add_breakpoint(bp)
+
+    def set_comment(self, addr, comment_text):
+        kb = self.project.kb
+        exists = addr in kb.comments
+
+        # callback
+        if comment_text is None and exists:
+            self.workspace.plugins.handle_comment_changed(addr, "", False, False, False)
+            del kb.comments[addr]
+        else:
+            self.workspace.plugins.handle_comment_changed(addr, comment_text, not exists, False, False)
+            kb.comments[addr] = comment_text
+
+        # TODO: can this be removed?
+        if self.set_comment_callback:
+            self.set_comment_callback(addr=addr, comment_text=comment_text)
+
+    def run_analysis(self, prompt_for_configuration=True):
+        if self.project.am_none:
+            return
+
+        if self._analysis_configuration is None:
+            self._analysis_configuration = AnalysesConfiguration(
+                [
+                    a(self)
+                    for a in [CFGAnalysisConfiguration, FlirtAnalysisConfiguration, VariableRecoveryConfiguration]
+                ], self)
+
+        if not self.workspace.main_window.shown_at_start:
+            # If we are running headlessly (e.g. tests), just run with default configuration
+            prompt_for_configuration = False
+
+        if prompt_for_configuration:
+            dlg = AnalysisOptionsDialog(self._analysis_configuration, self.workspace, self.workspace.main_window)
+            dlg.setModal(True)
+            should_run = dlg.exec_()
+        else:
+            should_run = True
+
+        if should_run:
+            if self._analysis_configuration['cfg'].enabled:
+                self.generate_cfg(self._analysis_configuration['cfg'].to_dict())
+
     #
     # Private methods
     #
@@ -303,3 +409,149 @@ def _update_simgr_debuggers(self, **kwargs):  # pylint:disable=unused-argument
                 self.debugger_mgr.set_debugger(dbg)
         elif sim_dbg is not None:
             self.debugger_list_mgr.remove_debugger(sim_dbg)
+
+    #
+    # Events
+    #
+
+    def generate_cfg(self, cfg_args=None):
+        if cfg_args is None:
+            cfg_args = {}
+
+        cfg_job = CFGGenerationJob(
+            on_finish=self.on_cfg_generated,
+            **cfg_args
+        )
+        self.add_job(cfg_job)
+        self._start_daemon_thread(self._refresh_cfg, 'Progressively Refreshing CFG', args=(cfg_job,))
+
+    def _refresh_cfg(self, cfg_job):
+        """
+        Reload once and then refresh in a loop, while the CFG job is running
+        """
+        reloaded = False
+        while True:
+            if not self.cfg.am_none:
+                if reloaded:
+                    gui_thread_schedule_async(self.workspace.refresh,
+                                              kwargs={'categories': ['disassembly', 'functions'],}
+                                              )
+                else:
+                    gui_thread_schedule_async(self.workspace.reload,
+                                              kwargs={'categories': ['disassembly', 'functions'],}
+                                              )
+                    reloaded = True
+
+            time.sleep(0.3)
+            if cfg_job not in self.jobs:
+                break
+
+    def on_cfg_generated(self):
+        if self._analysis_configuration['flirt'].enabled:
+            self.add_job(
+                FlirtSignatureRecognitionJob(
+                    on_finish=self._on_flirt_signature_recognized,
+                )
+            )
+
+        # display the main function if it exists, otherwise display the function at the entry point
+        if self.cfg is not None:
+            the_func = self.kb.functions.function(name='main')
+            if the_func is None:
+                the_func = self.kb.functions.function(addr=self.project.entry)
+
+            if the_func is not None:
+                self.on_function_selected(the_func)
+
+            # Initialize the linear viewer
+            if len(self.workspace.view_manager.views_by_category['disassembly']) == 1:
+                view = self.workspace.view_manager.first_view_in_category('disassembly')
+            else:
+                view = self.workspace.view_manager.current_view_in_category('disassembly')
+            if view is not None:
+                view._linear_viewer.initialize()
+
+            # Reload the pseudocode view
+            view = self.workspace.view_manager.first_view_in_category('pseudocode')
+            if view is not None:
+                view.reload()
+
+            # Reload the strings view
+            view = self.workspace.view_manager.first_view_in_category('strings')
+            if view is not None:
+                view.reload()
+
+            # Clear the proximity view
+            view = self.workspace.view_manager.first_view_in_category('proximity')
+            if view is not None:
+                view.clear()
+
+    def _on_flirt_signature_recognized(self):
+        self.add_job(
+            PrototypeFindingJob(
+                on_finish=self._on_prototype_found,
+            )
+        )
+
+    def _on_prototype_found(self):
+        self.add_job(
+            CodeTaggingJob(
+                on_finish=self.on_function_tagged,
+            )
+        )
+
+        if self._analysis_configuration['varec'].enabled:
+            workers = 4 if not is_testing else 0  # disable multiprocessing on angr CI
+            self.variable_recovery_job = VariableRecoveryJob(
+                **self._analysis_configuration['varec'].to_dict(),
+                on_variable_recovered=self.on_variable_recovered,
+                workers=workers,
+            )
+            # prioritize the current function in display
+            disassembly_view = self.workspace.view_manager.first_view_in_category("disassembly")
+            if disassembly_view is not None:
+                if not disassembly_view.function.am_none:
+                    self.variable_recovery_job.prioritize_function(disassembly_view.function.addr)
+            self.add_job(self.variable_recovery_job)
+
+    def on_function_selected(self, func: Function):
+        """
+        Callback function triggered when a new function is selected in the function view.
+
+        :param func:    The function that is selected.
+        :return:        None
+        """
+
+        # Ask all current views to display this function
+        current_view = self.workspace.view_manager.current_tab
+        if current_view is None or not current_view.FUNCTION_SPECIFIC_VIEW:
+            # we don't have a current view or the current view does not have function-specific content. create a
+            # disassembly view to display the selected function.
+            disasm_view = self.workspace._get_or_create_disassembly_view()
+            #disasm_view = DisassemblyView(self, 'center')
+            disasm_view.display_function(func)
+            self.workspace.view_manager.raise_view(disasm_view)
+        else:
+            # ask the current view to display this function
+            current_view.function = func
+
+    def on_function_tagged(self):
+        # reload disassembly view
+        if len(self.workspace.view_manager.views_by_category['disassembly']) == 1:
+            view = self.workspace.view_manager.first_view_in_category('disassembly')
+        else:
+            view = self.workspace.view_manager.current_view_in_category('disassembly')
+
+        if view is not None:
+            if view.current_function.am_obj is not None:
+                view.reload()
+
+    def on_variable_recovered(self, func_addr: int):
+        """
+        Called when variable information of the given function is available.
+
+        :param int func_addr:   Address of the function whose variable information is available.
+        """
+        disassembly_view = self.workspace.view_manager.first_view_in_category("disassembly")
+        if disassembly_view is not None:
+            disassembly_view.on_variable_recovered(func_addr)
diff --git a/angrmanagement/data/jobs/cfg_generation.py b/angrmanagement/data/jobs/cfg_generation.py
index 20fbf6ea4..ab981c471 100644
--- a/angrmanagement/data/jobs/cfg_generation.py
+++ b/angrmanagement/data/jobs/cfg_generation.py
@@ -33,6 +33,7 @@ def __init__(self, on_finish=None, **kwargs):
         self._last_progress_callback_triggered = None
 
     def _run(self, inst):
+        self.instance = inst
         exclude_region_types = {'kernel', 'tls'}
         # create a temporary CFB for displaying partially analyzed binary during CFG recovery
         temp_cfb = inst.project.analyses.CFB(exclude_region_types=exclude_region_types)
@@ -84,5 +85,6 @@ def _progress_callback(self, percentage, text=None, cfg=None):
 
     def _refresh(self, cfg, cfb):
         # do not signal events. that will happen on a timer to not overwhelm the renderer
-        GlobalInfo.main_window.workspace.instance.cfg = cfg
-        GlobalInfo.main_window.workspace.instance.cfb = cfb
+        # instance will exist because _run must be used first
+        self.instance.cfg = cfg
+        self.instance.cfb = cfb
diff --git a/angrmanagement/data/jobs/job.py b/angrmanagement/data/jobs/job.py
index 3aeabd87b..68b0d532f 100644
--- a/angrmanagement/data/jobs/job.py
+++ b/angrmanagement/data/jobs/job.py
@@ -58,8 +58,8 @@ def finish(self, inst, result): #pylint: disable=unused-argument
     def keyboard_interrupt(self):
         """Called from the GUI thread when the user presses Ctrl+C or presses a cancel button"""
         # lol. lmao even.
-        if GlobalInfo.main_window.workspace.instance.current_job == self:
-            tid = GlobalInfo.main_window.workspace.instance.worker_thread.ident
+        if GlobalInfo.main_window.workspace.main_instance.current_job == self:
+            tid = GlobalInfo.main_window.workspace.main_instance.worker_thread.ident
             res = ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), ctypes.py_object(KeyboardInterrupt))
             if res != 1:
                 ctypes.pythonapi.PyThreadState_SetAsyncExc(ctypes.c_long(tid), 0)
diff --git a/angrmanagement/logic/debugger/bintrace.py b/angrmanagement/logic/debugger/bintrace.py
index 4b2ae9340..77093cbf1 100644
--- a/angrmanagement/logic/debugger/bintrace.py
+++ b/angrmanagement/logic/debugger/bintrace.py
@@ -33,7 +33,7 @@ def __init__(self, trace: BintraceTrace, workspace: 'Workspace'):
         assert isinstance(trace, BintraceTrace)
         self._trace: BintraceTrace = trace
         self._btrace: bintrace.Trace = trace.trace
-        self._trace_dbg: AngrTraceDebugger = AngrTraceDebugger(self._btrace, self.workspace.instance.project.am_obj)
+        self._trace_dbg: AngrTraceDebugger = AngrTraceDebugger(self._btrace, self.workspace.main_instance.project.am_obj)
         self._cached_simstate = None
 
     def __str__(self):
@@ -58,7 +58,7 @@ def _sync_breakpoints(self):
         }
         self._trace_dbg.breakpoints = {
             bintrace.debugger.Breakpoint(bp_type_map[bp.type], bp.addr, bp.size)
-            for bp in self.workspace.instance.breakpoint_mgr.breakpoints
+            for bp in self.workspace.main_instance.breakpoint_mgr.breakpoints
         }
 
     @property
@@ -182,11 +182,11 @@ def get_function_for_event(self, event: TraceEvent) -> Optional[Function]:
             return None
 
         # Determine what function we are in currently.
-        node = self.workspace.instance.cfg.get_node(event.Addr())
+        node = self.workspace.main_instance.cfg.get_node(event.Addr())
         if node is None:
             return None
 
-        kb = self.workspace.instance.project.kb
+        kb = self.workspace.main_instance.project.kb
         if node.function_address in kb.functions:
             return kb.functions[node.function_address], event
         else:
@@ -240,7 +240,7 @@ def get_called_functions(self, event: Optional[TraceEvent] = None,
 
             # Check exit type
             exit_block_addr = event.Addr()
-            b = self.workspace.instance.project.factory.block(exit_block_addr)
+            b = self.workspace.main_instance.project.factory.block(exit_block_addr)
             if b.vex.jumpkind == 'Ijk_Ret':
                 _l.debug('Exit is a return to caller')
                 break
@@ -256,7 +256,7 @@ def get_called_functions(self, event: Optional[TraceEvent] = None,
             event = called_func_entry_event
             ret_addr = b.instruction_addrs[0] + b.size
             num_nested_calls = 0
-            if self.workspace.instance.project.arch.name == 'AMD64':
+            if self.workspace.main_instance.project.arch.name == 'AMD64':
                 # FIXME Remove this hardcoding
                 stack_reg = 7
                 expected_sp = called_func_entry_event.Regs(stack_reg) + 8
@@ -282,7 +282,7 @@ def get_called_functions(self, event: Optional[TraceEvent] = None,
                 _l.debug('Skipping over nested call (%d)', num_nested_calls)
                 num_nested_calls += 1
 
-        all_funcs = self.workspace.instance.project.kb.functions
+        all_funcs = self.workspace.main_instance.project.kb.functions
         return [((all_funcs[addr] if (addr in all_funcs) else addr), e) for (addr, e) in called_addrs]
 
     def get_called_functions_recursive(self, event: Optional[TraceEvent] = None,
diff --git a/angrmanagement/logic/debugger/debugger.py b/angrmanagement/logic/debugger/debugger.py
index 1f9bc57c0..b0e81ee93 100644
--- a/angrmanagement/logic/debugger/debugger.py
+++ b/angrmanagement/logic/debugger/debugger.py
@@ -15,7 +15,7 @@ class Debugger:
     def __init__(self, workspace: 'Workspace'):
         super().__init__()
         self.workspace: 'Workspace' = workspace
-        self.instance: 'Instance' = workspace.instance
+        self.instance: 'Instance' = workspace.main_instance
         self.state_changed: EventSentinel = EventSentinel()
 
     @property
diff --git a/angrmanagement/plugins/ail2asm/ail2arm32.py b/angrmanagement/plugins/ail2asm/ail2arm32.py
index 01cb73088..591d19135 100644
--- a/angrmanagement/plugins/ail2asm/ail2arm32.py
+++ b/angrmanagement/plugins/ail2asm/ail2arm32.py
@@ -49,10 +49,10 @@ def opcode_variant(opcode, size=None, signed=None, cond=None):
     def bp_offset_to_sp_offset(self, offset_from_bp, function_addr, expr_addr, adjust=0):
         if function_addr is None or expr_addr is None:
             raise Exception("function_addr and expr_addr must be specified")
-        sp = self.workspace.instance.project.arch.sp_offset
-        bp = self.workspace.instance.project.arch.bp_offset
-        sptracker = self.workspace.instance.project.analyses.StackPointerTracker(
-            self.workspace.instance.project.kb.functions[function_addr], {sp, bp}
+        sp = self.workspace.main_instance.project.arch.sp_offset
+        bp = self.workspace.main_instance.project.arch.bp_offset
+        sptracker = self.workspace.main_instance.project.analyses.StackPointerTracker(
+            self.workspace.main_instance.project.kb.functions[function_addr], {sp, bp}
             )
         sp_offset = sptracker.offset_after(expr_addr, sp)
         bp_offset = sptracker.offset_after(expr_addr, bp)
diff --git a/angrmanagement/plugins/angr_binsync/binsync_plugin.py b/angrmanagement/plugins/angr_binsync/binsync_plugin.py
index b9b875961..282ffcdcb 100644
--- a/angrmanagement/plugins/angr_binsync/binsync_plugin.py
+++ b/angrmanagement/plugins/angr_binsync/binsync_plugin.py
@@ -57,7 +57,7 @@ def handle_click_menu(self, idx):
         if idx < 0 or idx >= len(self.MENU_BUTTONS):
             return
 
-        if self.workspace.instance.project.am_none:
+        if self.workspace.main_instance.project.am_none:
             return
 
         mapping = {
@@ -70,7 +70,7 @@ def handle_click_menu(self, idx):
 
 
     def open_sync_config_dialog(self):
-        if self.workspace.instance.project.am_none:
+        if self.workspace.main_instance.project.am_none:
             # project does not exist yet
             return
 
diff --git a/angrmanagement/plugins/angr_binsync/controller.py b/angrmanagement/plugins/angr_binsync/controller.py
index 3adda555d..826cb6e3c 100644
--- a/angrmanagement/plugins/angr_binsync/controller.py
+++ b/angrmanagement/plugins/angr_binsync/controller.py
@@ -27,7 +27,7 @@ class AngrBinSyncController(BinSyncController):
     def __init__(self, workspace):
         super().__init__()
         self._workspace = workspace
-        self._instance = workspace.instance
+        self._instance = workspace.main_instance
 
     def binary_hash(self) -> str:
         return self._instance.project.loader.main_object.md5.hex()
@@ -247,7 +247,7 @@ def func_insn_addrs(func: angr.knowledge_plugins.Function):
 
     def get_func_addr_from_addr(self, addr):
         try:
-            func_addr = self._workspace.instance.kb.cfgs.get_most_accurate()\
+            func_addr = self._workspace.main_instance.kb.cfgs.get_most_accurate()\
                 .get_any_node(addr, anyaddr=True)\
                 .function_address
         except AttributeError:
diff --git a/angrmanagement/plugins/bughouse/components.py b/angrmanagement/plugins/bughouse/components.py
index 113a640c5..41de0d46e 100644
--- a/angrmanagement/plugins/bughouse/components.py
+++ b/angrmanagement/plugins/bughouse/components.py
@@ -42,7 +42,7 @@ def handle_click_menu(self, idx):
         if idx < 0 or idx >= len(self.MENU_BUTTONS):
             return
 
-        if self.workspace.instance.project.am_none:
+        if self.workspace.main_instance.project.am_none:
             return
 
         mapping = {
diff --git a/angrmanagement/plugins/bughouse/ui/components_treeview.py b/angrmanagement/plugins/bughouse/ui/components_treeview.py
index 31a2ee323..bb7c2eb83 100644
--- a/angrmanagement/plugins/bughouse/ui/components_treeview.py
+++ b/angrmanagement/plugins/bughouse/ui/components_treeview.py
@@ -76,10 +76,10 @@ def load(self, tree: ComponentTree):
         Display a component tree.
         """
 
-        if self.workspace.instance.project.am_none:
+        if self.workspace.main_instance.project.am_none:
             return
-        proj = self.workspace.instance.project
-        funcs = self.workspace.instance.kb.functions
+        proj = self.workspace.main_instance.project
+        funcs = self.workspace.main_instance.kb.functions
 
         self._tree.clear()
 
@@ -99,7 +99,7 @@ def load(self, tree: ComponentTree):
                     func = funcs.get_by_addr(proj.loader.main_object.mapped_base + comp_func.virtual_addr)
                 except KeyError:
                     func = None
-                func_node = QFunctionItem(self.workspace.instance.project, item, comp_func, function=func)
+                func_node = QFunctionItem(self.workspace.main_instance.project, item, comp_func, function=func)
                 item.function_nodes.append(func_node)
             # insert all components into the queue
             for comp in node.components:
diff --git a/angrmanagement/plugins/bughouse/ui/load_components_dialog.py b/angrmanagement/plugins/bughouse/ui/load_components_dialog.py
index cbc115af9..011b9562a 100644
--- a/angrmanagement/plugins/bughouse/ui/load_components_dialog.py
+++ b/angrmanagement/plugins/bughouse/ui/load_components_dialog.py
@@ -157,9 +157,9 @@ def _load_json(self, data: List[Dict]):
                 if blob_type != "bin":
                     raise TypeError("Unsupported blob_type \"%s\"." % blob_type)
                 file_hash = binary.get('file_hash')
-                if self.workspace.instance.project.am_none:
+                if self.workspace.main_instance.project.am_none:
                     raise ValueError("No project has been loaded.")
-                sha256 = self.workspace.instance.project.loader.main_object.sha256
+                sha256 = self.workspace.main_instance.project.loader.main_object.sha256
                 if binascii.unhexlify(file_hash) != sha256:
                     # warn user about it
                     r = QMessageBox.warning(None,
diff --git a/angrmanagement/plugins/chess_manager/chess_connector.py b/angrmanagement/plugins/chess_manager/chess_connector.py
index b6c8eef53..7a7033b03 100644
--- a/angrmanagement/plugins/chess_manager/chess_connector.py
+++ b/angrmanagement/plugins/chess_manager/chess_connector.py
@@ -138,7 +138,7 @@ def target_id_updated(self):
             self._target_description_label.setToolTip(f"Target ID: {self.target_id}\n"
                                                       f"Target image ID: {self.target_image_id}")
 
-            DaemonClient.register_binary(self.workspace.instance.project.loader.main_object.binary,
+            DaemonClient.register_binary(self.workspace.main_instance.project.loader.main_object.binary,
                                          self.target_id)
         else:
             self._target_description_label.setText("No associated CHESS target")
@@ -230,7 +230,7 @@ def set_chess_target(self):
                                  QMessageBox.Ok)
             return
 
-        if self.workspace.instance.project.am_none:
+        if self.workspace.main_instance.project.am_none:
             QMessageBox.critical(self.workspace.main_window,
                                  "No binary is loaded",
                                  "Please load a binary before associating it to a CHESS target.",
diff --git a/angrmanagement/plugins/chess_manager/poi_plugin.py b/angrmanagement/plugins/chess_manager/poi_plugin.py
index fc5e9c69a..84b2f8b2b 100644
--- a/angrmanagement/plugins/chess_manager/poi_plugin.py
+++ b/angrmanagement/plugins/chess_manager/poi_plugin.py
@@ -26,9 +26,9 @@ class POIViewer(BasePlugin):
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
 
-        self.workspace.instance.register_container('poi_trace', lambda: None, Optional[TraceStatistics],
+        self.workspace.main_instance.register_container('poi_trace', lambda: None, Optional[TraceStatistics],
                                                    'The trace of selected POI')
-        self.workspace.instance.register_container('multi_poi', lambda: None, Optional[MultiPOI],
+        self.workspace.main_instance.register_container('multi_poi', lambda: None, Optional[MultiPOI],
                                                    'POI list')
 
         self._views = []
@@ -52,11 +52,11 @@ def teardown(self):
 
     @property
     def poi_trace(self) -> Union[ObjectContainer, Optional[TraceStatistics]]:
-        return self.workspace.instance.poi_trace
+        return self.workspace.main_instance.poi_trace
 
     @property
     def multi_poi(self) -> Union[ObjectContainer, Optional[MultiPOI]]:
-        return self.workspace.instance.multi_poi
+        return self.workspace.main_instance.multi_poi
 
 
     #
diff --git a/angrmanagement/plugins/chess_manager/qpoi_viewer.py b/angrmanagement/plugins/chess_manager/qpoi_viewer.py
index 7b2adaf92..467b256cc 100644
--- a/angrmanagement/plugins/chess_manager/qpoi_viewer.py
+++ b/angrmanagement/plugins/chess_manager/qpoi_viewer.py
@@ -107,11 +107,11 @@ def disasm_view(self):
 
     @property
     def poi_trace(self):
-        return self.workspace.instance.poi_trace
+        return self.workspace.main_instance.poi_trace
 
     @property
     def multi_poi(self):
-        return self.workspace.instance.multi_poi
+        return self.workspace.main_instance.multi_poi
 
     @property
     def selected_ins(self):
@@ -343,11 +343,11 @@ def _subscribe_select_ins(self, **kwargs): # pylint: disable=unused-argument
                 self.scroll_to_position(self.curr_position)
 
     def _get_func_from_addr(self, addr):
-        if self.workspace.instance.cfg.am_none:
+        if self.workspace.main_instance.cfg.am_none:
             return None
-        bbl = self.workspace.instance.cfg.get_any_node(addr, anyaddr=True)
+        bbl = self.workspace.main_instance.cfg.get_any_node(addr, anyaddr=True)
         function_addr = bbl.function_address
-        return self.workspace.instance.project.kb.functions.get(function_addr)
+        return self.workspace.main_instance.project.kb.functions.get(function_addr)
 
     def _populate_poi_table(self, view, poi_ids):
         view.clearContents()
@@ -451,7 +451,7 @@ def mousePressEvent(self, event):
     def _jump_bbl(self, func, bbl_addr):
         disasm_view = self.disasm_view
         if disasm_view is not None:
-            all_insn_addrs = self.workspace.instance.project.factory.block(bbl_addr).instruction_addrs
+            all_insn_addrs = self.workspace.main_instance.project.factory.block(bbl_addr).instruction_addrs
             # TODO: replace this with am_events perhaps?
             self.workspace.on_function_selected(func)
             self.selected_ins.clear()
diff --git a/angrmanagement/plugins/chess_manager/trace_statistics.py b/angrmanagement/plugins/chess_manager/trace_statistics.py
index 137a5a5bb..de1b15850 100644
--- a/angrmanagement/plugins/chess_manager/trace_statistics.py
+++ b/angrmanagement/plugins/chess_manager/trace_statistics.py
@@ -39,7 +39,7 @@ def __init__(self, workspace, trace, trace_id=None, baddr=None):
         self._positions = defaultdict(list)
         self.mapped_trace = []
 
-        project = self.workspace.instance.project
+        project = self.workspace.main_instance.project
         if project.am_none:
             self.project_baddr = None
         else:
@@ -105,19 +105,19 @@ def _statistics(self, trace_addrs):
         bbls = filter(self._get_bbl, self.mapped_trace)
 
         for p, bbl_addr in enumerate(bbls):
-            block = self.workspace.instance.project.factory.block(bbl_addr)
+            block = self.workspace.main_instance.project.factory.block(bbl_addr)
             for addr in block.instruction_addrs:
                 self._positions[addr].append(p)
 
-            node = self.workspace.instance.cfg.get_any_node(bbl_addr)
+            node = self.workspace.main_instance.cfg.get_any_node(bbl_addr)
             if node is None: #try again without asssuming node is start of a basic block
-                node = self.workspace.instance.cfg.get_any_node(bbl_addr, anyaddr=True)
+                node = self.workspace.main_instance.cfg.get_any_node(bbl_addr, anyaddr=True)
 
             func_name = hex(bbl_addr) #default to using bbl_addr as name if none is not found
             func = None
             if node is not None:
                 func_addr = node.function_address
-                functions = self.workspace.instance.project.kb.functions
+                functions = self.workspace.main_instance.project.kb.functions
                 if func_addr in functions:
                     func_name = functions[func_addr].demangled_name
                     func = functions[func_addr]
@@ -131,15 +131,15 @@ def _statistics(self, trace_addrs):
 
     def _get_bbl(self, addr):
         try:
-            return self.workspace.instance.project.factory.block(addr)
+            return self.workspace.main_instance.project.factory.block(addr)
         except SimEngineError:
             return None
 
     def _func_addr(self, a):
-        return self.workspace.instance.cfg.get_any_node(a).function_address
+        return self.workspace.main_instance.cfg.get_any_node(a).function_address
 
     def _func_name(self, a):
-        return self.workspace.instance.project.kb.functions[self._func_addr(a)].demangled_name
+        return self.workspace.main_instance.project.kb.functions[self._func_addr(a)].demangled_name
 
     @staticmethod
     def _random_color():
diff --git a/angrmanagement/plugins/coverage/coverage.py b/angrmanagement/plugins/coverage/coverage.py
index 9b4312447..b44bad879 100644
--- a/angrmanagement/plugins/coverage/coverage.py
+++ b/angrmanagement/plugins/coverage/coverage.py
@@ -216,7 +216,7 @@ def update_one_coverage(self, trace):
             l.exception("Unable to parse %s as JSON.", url)
             return
 
-        bbl_addrs = trace_to_bb_addrs(parsed_trace, self.workspace.instance.project, TRACE_BASE)
+        bbl_addrs = trace_to_bb_addrs(parsed_trace, self.workspace.main_instance.project, TRACE_BASE)
         self.update_coverage_from_list(bbl_addrs)
 
         with self.coverage_lock:
diff --git a/angrmanagement/plugins/decompiler_poison/__init__.py b/angrmanagement/plugins/decompiler_poison/__init__.py
index de0548803..2dbb7e2ac 100644
--- a/angrmanagement/plugins/decompiler_poison/__init__.py
+++ b/angrmanagement/plugins/decompiler_poison/__init__.py
@@ -113,7 +113,7 @@ def build_context_menu_node(self, node):
 
     @property
     def knowledge(self) -> 'PoisonKnowledge':
-        return self.workspace.instance.kb.decompiler_poison
+        return self.workspace.main_instance.kb.decompiler_poison
 
     def set_poison_local(self, func, callee, value):
         if value:
@@ -140,18 +140,18 @@ def _string_to_poison(a_string):
         return {int(a, 16) for a in a_string.split(',')}
 
     def angrdb_store_entries(self):
-        poison = self.workspace.instance.kb.decompiler_poison.global_poison
+        poison = self.workspace.main_instance.kb.decompiler_poison.global_poison
         if poison:
             yield ('global_poison', self._poison_to_string(poison))
-        for func, poison in self.workspace.instance.kb.decompiler_poison.local_poison.items():
+        for func, poison in self.workspace.main_instance.kb.decompiler_poison.local_poison.items():
             if poison:
                 yield ('local_poison_' + hex(func), self._poison_to_string(poison))
 
     def angrdb_load_entry(self, key: str, value: str):
         if key == 'global_poison':
-            self.workspace.instance.kb.decompiler_poison.global_poison = self._string_to_poison(value)
+            self.workspace.main_instance.kb.decompiler_poison.global_poison = self._string_to_poison(value)
         elif key.startswith('local_poison_'):
             func = int(key.split('_')[2], 16)
-            self.workspace.instance.kb.decompiler_poison.local_poison[func] = self._string_to_poison(value)
+            self.workspace.main_instance.kb.decompiler_poison.local_poison[func] = self._string_to_poison(value)
 
 PoisonKnowledge.register_default('decompiler_poison', PoisonKnowledge)
diff --git a/angrmanagement/plugins/execution_statistics_viewer/execution_statistics_viewer.py b/angrmanagement/plugins/execution_statistics_viewer/execution_statistics_viewer.py
index 28ce8734a..c227a2739 100644
--- a/angrmanagement/plugins/execution_statistics_viewer/execution_statistics_viewer.py
+++ b/angrmanagement/plugins/execution_statistics_viewer/execution_statistics_viewer.py
@@ -22,7 +22,7 @@ def __init__(self, *args, **kwargs):
         self.addr_to_active_states = defaultdict(list)
         self.returning_to_here_states = defaultdict(list)
         self.bb_addrs = None
-        self.instance = self.workspace.instance
+        self.instance = self.workspace.main_instance
 
         if self.symexec_view:
             self.current_simgr = self.symexec_view.current_simgr
diff --git a/angrmanagement/plugins/interaction_console/interaction_console.py b/angrmanagement/plugins/interaction_console/interaction_console.py
index 097970169..80141aa3e 100644
--- a/angrmanagement/plugins/interaction_console/interaction_console.py
+++ b/angrmanagement/plugins/interaction_console/interaction_console.py
@@ -49,7 +49,7 @@ def __init__(self, workspace, *args, **kwargs):
     def connect(self):
         self.disconnect()
 
-        img_name = self.workspace.instance.img_name
+        img_name = self.workspace.main_instance.img_name
         if img_name is None:
             QMessageBox.critical(
                 None, "Nothing to run", "The project was not loaded from a docker image"
@@ -125,10 +125,10 @@ def direction(srcport, dstport):
 
         name = hex(hash(str(conversation)) & 0xFFFFFFFFFFFFFFFF)[2:].rjust(16, "0")
 
-        self.workspace.instance.interactions.am_obj.append(
+        self.workspace.main_instance.interactions.am_obj.append(
             SavedInteraction(name, PlainTextProtocol, log)
         )
-        self.workspace.instance.interactions.am_event()
+        self.workspace.main_instance.interactions.am_event()
 
 
 class InteractionConsole(BasePlugin):
diff --git a/angrmanagement/plugins/log_human_activities/log_human_activities.py b/angrmanagement/plugins/log_human_activities/log_human_activities.py
index 9df8548d1..4d453db24 100644
--- a/angrmanagement/plugins/log_human_activities/log_human_activities.py
+++ b/angrmanagement/plugins/log_human_activities/log_human_activities.py
@@ -155,12 +155,12 @@ def handle_project_initialization(self):
         """
         Set project name
         """
-        if self.workspace.instance.img_name is not None:
-            self.project_name = self.workspace.instance.img_name
+        if self.workspace.main_instance.img_name is not None:
+            self.project_name = self.workspace.main_instance.img_name
         else:
-            filename = self.workspace.instance.project.filename
+            filename = self.workspace.main_instance.project.filename
             self.project_name = filename
-            self.project_md5 = self.workspace.instance.project.loader.main_object.md5.hex()
+            self.project_md5 = self.workspace.main_instance.project.loader.main_object.md5.hex()
             l.debug("Set project md5 to %s", self.project_md5)
         l.debug("Set project name to %s", self.project_name)
 
diff --git a/angrmanagement/plugins/log_reverse_engineering/log_reverse_engineering_plugin.py b/angrmanagement/plugins/log_reverse_engineering/log_reverse_engineering_plugin.py
index 4fed86f6c..893f918a5 100644
--- a/angrmanagement/plugins/log_reverse_engineering/log_reverse_engineering_plugin.py
+++ b/angrmanagement/plugins/log_reverse_engineering/log_reverse_engineering_plugin.py
@@ -25,9 +25,9 @@ def __init__(self, workspace):
         super().__init__(workspace)
         self.session = Slacrs(database=Conf.checrs_backend_str).session()
         self.project = (
-            self.workspace.instance.img_name
-            if self.workspace.instance.img_name
-            else self.workspace.instance.project.filename
+            self.workspace.main_instance.img_name
+            if self.workspace.main_instance.img_name
+            else self.workspace.main_instance.project.filename
         )
 
     def handle_stack_var_renamed(self, func, offset, old_name, new_name):
@@ -85,7 +85,7 @@ def handle_project_save(self, file_name: str):
             .all()
         )
         total_variables_count = len(
-            self.workspace.instance.project.kb.variables.global_manager._variables
+            self.workspace.main_instance.project.kb.variables.global_manager._variables
         )
         reverse_eng_progress = (
             self.session.query(ReverseEngineeringProgress)
@@ -125,14 +125,14 @@ def get_function_rename_stats(self):
         functions_renamed_count = 0
         total_functions_count = 0
 
-        for key in self.workspace.instance.project.kb.functions._function_map:
+        for key in self.workspace.main_instance.project.kb.functions._function_map:
             if (
-                self.workspace.instance.project.kb.functions._function_map[key]._name
+                self.workspace.main_instance.project.kb.functions._function_map[key]._name
                 in functions_renamed
             ):
                 functions_renamed_count = functions_renamed_count + 1
                 total_functions_count = total_functions_count + 1
-            elif self.workspace.instance.project.kb.functions._function_map[
+            elif self.workspace.main_instance.project.kb.functions._function_map[
                 key
             ]._name.startswith("sub"):
                 total_functions_count = total_functions_count + 1
diff --git a/angrmanagement/plugins/memory_checker/memory_checker.py b/angrmanagement/plugins/memory_checker/memory_checker.py
index 9daea24a0..9ce290262 100644
--- a/angrmanagement/plugins/memory_checker/memory_checker.py
+++ b/angrmanagement/plugins/memory_checker/memory_checker.py
@@ -14,7 +14,7 @@ class MemoryChecker(BasePlugin):
 
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
-        self.states = self.workspace.instance.states
+        self.states = self.workspace.main_instance.states
         self.states.am_subscribe(self.install_state_plugin)
 
     def install_state_plugin(self, **kwargs):
diff --git a/angrmanagement/plugins/sample_plugin.py b/angrmanagement/plugins/sample_plugin.py
index 3ebf8eb65..7a424520c 100644
--- a/angrmanagement/plugins/sample_plugin.py
+++ b/angrmanagement/plugins/sample_plugin.py
@@ -9,7 +9,7 @@ class SamplePlugin(BasePlugin):
     def __init__(self, workspace):
         super().__init__(workspace)
 
-        workspace.instance.register_container('bookmarks', lambda: [], List[int], 'Bookmarked addresses')
+        workspace.main_instance.register_container('bookmarks', lambda: [], List[int], 'Bookmarked addresses')
 
     MENU_BUTTONS = ('Add Bookmark',)
 
diff --git a/angrmanagement/plugins/seed_table/seed_table_plugin.py b/angrmanagement/plugins/seed_table/seed_table_plugin.py
index 795dc4e31..aa58a872a 100644
--- a/angrmanagement/plugins/seed_table/seed_table_plugin.py
+++ b/angrmanagement/plugins/seed_table/seed_table_plugin.py
@@ -200,8 +200,8 @@ def __init__(self, workspace: Workspace, *args, **kwargs):
         super().__init__("SeedTableView", workspace, *args, **kwargs)
         self.base_caption = "Seed Table"
         self.workspace = workspace
-        self.instance = workspace.instance
-        workspace.instance.project.am_subscribe(self.on_project_load)
+        self.instance = workspace.main_instance
+        workspace.main_instance.project.am_subscribe(self.on_project_load)
         self._init_widgets()
 
     def page_changed(self, i):
diff --git a/angrmanagement/plugins/source_importer/__init__.py b/angrmanagement/plugins/source_importer/__init__.py
index 974efaecf..26a22d7e8 100644
--- a/angrmanagement/plugins/source_importer/__init__.py
+++ b/angrmanagement/plugins/source_importer/__init__.py
@@ -23,12 +23,12 @@ def handle_project_initialization(self):
 
     def _import_from_project(self):
         self.source_paths = []
-        if self.workspace.instance.original_binary_path:
-            self.source_paths.append(os.path.dirname(self.workspace.instance.original_binary_path))
+        if self.workspace.main_instance.original_binary_path:
+            self.source_paths.append(os.path.dirname(self.workspace.main_instance.original_binary_path))
 
     def decompile_callback(self, func):
         for source_root in self.source_paths:
-            self.workspace.instance.project.analyses.ImportSourceCode(func, flavor='source', source_root=source_root)
+            self.workspace.main_instance.project.analyses.ImportSourceCode(func, flavor='source', source_root=source_root)
 
     MENU_BUTTONS = ['Import source path']
 
diff --git a/angrmanagement/plugins/source_viewer/source_viewer_plugin.py b/angrmanagement/plugins/source_viewer/source_viewer_plugin.py
index 891008cf3..ad3d91fdd 100644
--- a/angrmanagement/plugins/source_viewer/source_viewer_plugin.py
+++ b/angrmanagement/plugins/source_viewer/source_viewer_plugin.py
@@ -234,8 +234,8 @@ def __init__(self, workspace: Workspace, *args, **kwargs):
         super().__init__("SourceViewer", workspace, *args, **kwargs)
         self.base_caption = "Source Viewer"
         self.workspace = workspace
-        self.instance = workspace.instance
-        workspace.instance.project.am_subscribe(self.load_from_proejct)
+        self.instance = workspace.main_instance
+        workspace.main_instance.project.am_subscribe(self.load_from_proejct)
         self._init_widgets()
 
     @property
diff --git a/angrmanagement/plugins/trace_viewer/afl_qemu_bitmap.py b/angrmanagement/plugins/trace_viewer/afl_qemu_bitmap.py
index 6773b3fdd..122211b87 100644
--- a/angrmanagement/plugins/trace_viewer/afl_qemu_bitmap.py
+++ b/angrmanagement/plugins/trace_viewer/afl_qemu_bitmap.py
@@ -31,14 +31,14 @@ def __init__(self, workspace, bitmap, base_addr, bits_inverted=False):
         self._node_hitcounts = {}
         self._node_hitcount_summary = {}
 
-        project = self.workspace.instance.project
+        project = self.workspace.main_instance.project
         self.project_baddr = project.loader.main_object.mapped_base
         self.runtime_baddr = base_addr
 
         self._compute_hitcounts()
 
     def _compute_hitcounts(self):
-        func_manager = self.workspace.instance.project.kb.functions
+        func_manager = self.workspace.main_instance.project.kb.functions
         for func_addr in func_manager:
             func = func_manager[func_addr]
 
diff --git a/angrmanagement/plugins/trace_viewer/qtrace_viewer.py b/angrmanagement/plugins/trace_viewer/qtrace_viewer.py
index 4ee3f8044..2ee7530d1 100644
--- a/angrmanagement/plugins/trace_viewer/qtrace_viewer.py
+++ b/angrmanagement/plugins/trace_viewer/qtrace_viewer.py
@@ -71,11 +71,11 @@ def __init__(self, workspace, disasm_view, parent=None):
 
     @property
     def trace(self):
-        return self.workspace.instance.trace
+        return self.workspace.main_instance.trace
 
     @property
     def multi_trace(self):
-        return self.workspace.instance.multi_trace
+        return self.workspace.main_instance.multi_trace
 
     @property
     def selected_ins(self):
@@ -368,7 +368,7 @@ def mousePressEvent(self, event):
             self._jump_bbl(func, bbl_addr)
 
     def _jump_bbl(self, func, bbl_addr):
-        all_insn_addrs = self.workspace.instance.project.factory.block(bbl_addr).instruction_addrs
+        all_insn_addrs = self.workspace.main_instance.project.factory.block(bbl_addr).instruction_addrs
         # TODO: replace this with am_events perhaps?
         if func is None:
             return
diff --git a/angrmanagement/plugins/trace_viewer/trace_plugin.py b/angrmanagement/plugins/trace_viewer/trace_plugin.py
index 993c62c76..510ecd91f 100644
--- a/angrmanagement/plugins/trace_viewer/trace_plugin.py
+++ b/angrmanagement/plugins/trace_viewer/trace_plugin.py
@@ -26,9 +26,9 @@ class TraceViewer(BasePlugin):
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
 
-        self.workspace.instance.register_container('trace', lambda: None, Optional[TraceStatistics],
+        self.workspace.main_instance.register_container('trace', lambda: None, Optional[TraceStatistics],
                                                    'The current trace')
-        self.workspace.instance.register_container('multi_trace', lambda: None, Optional[MultiTrace],
+        self.workspace.main_instance.register_container('multi_trace', lambda: None, Optional[MultiTrace],
                                                    'The current set of multiple traces')
 
         # Register event callbacks
@@ -48,11 +48,11 @@ def teardown(self):
 
     @property
     def trace(self) -> Union[ObjectContainer, Optional[TraceStatistics]]:
-        return self.workspace.instance.trace
+        return self.workspace.main_instance.trace
 
     @property
     def multi_trace(self) -> Union[ObjectContainer, Optional[MultiTrace], AFLQemuBitmap]:
-        return self.workspace.instance.multi_trace
+        return self.workspace.main_instance.multi_trace
 
     #
     # Event handlers
@@ -223,7 +223,7 @@ def handle_click_menu(self, idx):
         if idx < 0 or idx >= len(self.MENU_BUTTONS):
             return
 
-        if self.workspace.instance.project.am_none:
+        if self.workspace.main_instance.project.am_none:
             return
 
         mapping = {
diff --git a/angrmanagement/plugins/trace_viewer/trace_statistics.py b/angrmanagement/plugins/trace_viewer/trace_statistics.py
index 02f84fb78..02318486d 100644
--- a/angrmanagement/plugins/trace_viewer/trace_statistics.py
+++ b/angrmanagement/plugins/trace_viewer/trace_statistics.py
@@ -68,7 +68,7 @@ def __init__(self, workspace, trace, baddr):
         self._positions = defaultdict(list)
         self.mapped_trace = []
 
-        self.project = self.workspace.instance.project
+        self.project = self.workspace.main_instance.project
         if self.project.am_none:
             self.project_baddr = None
         else:
@@ -189,10 +189,10 @@ def _statistics(self, trace_addrs):
                 self.mapped_trace.append(converted_addr)
 
         bbls = filter(self._get_bbl, self.mapped_trace)
-        functions = self.workspace.instance.project.kb.functions
+        functions = self.workspace.main_instance.project.kb.functions
 
         for p, bbl_addr in enumerate(bbls):
-            node = self.workspace.instance.cfg.get_any_node(bbl_addr)
+            node = self.workspace.main_instance.cfg.get_any_node(bbl_addr)
             # if node is None:  # try again without assuming node is start of a basic block
             #     node = self.workspace.instance.cfg.get_any_node(bbl_addr, anyaddr=True)
 
@@ -202,7 +202,7 @@ def _statistics(self, trace_addrs):
                     instr_addrs = node.instruction_addrs
                 else:
                     # relift
-                    block = self.workspace.instance.project.factory.block(bbl_addr)
+                    block = self.workspace.main_instance.project.factory.block(bbl_addr)
                     instr_addrs = block.instruction_addrs
                 for addr in instr_addrs:
                     self._positions[addr].append(p)
@@ -228,15 +228,15 @@ def _statistics(self, trace_addrs):
 
     def _get_bbl(self, addr):
         try:
-            return self.workspace.instance.project.factory.block(addr)
+            return self.workspace.main_instance.project.factory.block(addr)
         except SimEngineError:
             return None
 
     def _func_addr(self, a):
-        return self.workspace.instance.cfg.get_any_node(a).function_address
+        return self.workspace.main_instance.cfg.get_any_node(a).function_address
 
     def _func_name(self, a):
-        return self.workspace.instance.project.kb.functions[self._func_addr(a)].demangled_name
+        return self.workspace.main_instance.project.kb.functions[self._func_addr(a)].demangled_name
 
     @staticmethod
     def _random_color():
diff --git a/angrmanagement/plugins/varec/varec.py b/angrmanagement/plugins/varec/varec.py
index 0d1c5d8c7..2ff61fd23 100644
--- a/angrmanagement/plugins/varec/varec.py
+++ b/angrmanagement/plugins/varec/varec.py
@@ -38,7 +38,7 @@ def handle_click_menu(self, idx):
         if idx < 0 or idx >= len(self.MENU_BUTTONS):
             return
 
-        if self.workspace.instance.project.am_none:
+        if self.workspace.main_instance.project.am_none:
             return
 
         mapping = {
diff --git a/angrmanagement/ui/dialogs/breakpoint.py b/angrmanagement/ui/dialogs/breakpoint.py
index 27043d5ce..212d0ac24 100644
--- a/angrmanagement/ui/dialogs/breakpoint.py
+++ b/angrmanagement/ui/dialogs/breakpoint.py
@@ -11,6 +11,7 @@
 class BreakpointDialog(QDialog):
     """
     Dialog to edit breakpoints.
+    TODO: decouple breakpoints from workspace.instance (main_instance)
     """
 
     def __init__(self, breakpoint_: Breakpoint, workspace: 'Workspace', parent=None):
@@ -117,5 +118,5 @@ def _on_ok_clicked(self):
         self.breakpoint.addr = self._address_box.target
         self.breakpoint.size = self._get_size()
         self.breakpoint.comment = self._comment_box.text()
-        self.workspace.instance.breakpoint_mgr.breakpoints.am_event()
+        self.workspace.main_instance.breakpoint_mgr.breakpoints.am_event()
         self.accept()
diff --git a/angrmanagement/ui/dialogs/func_doc.py b/angrmanagement/ui/dialogs/func_doc.py
index 70da9d62f..bb5a22c76 100644
--- a/angrmanagement/ui/dialogs/func_doc.py
+++ b/angrmanagement/ui/dialogs/func_doc.py
@@ -2,14 +2,13 @@
 from PySide2.QtWidgets import QDialog, QVBoxLayout, QLabel, QGridLayout, QTextEdit
 
 from ...config import Conf
-from ...data.instance import Instance
 
 
 class FuncDocDialog(QDialog):
     """
     Implements the FuncDoc dialog.
     """
-    def __init__(self, instance: Instance, addr=None, name="", doc_tuple=None, parent=None):
+    def __init__(self, instance, addr=None, name="", doc_tuple=None, parent=None):
         super().__init__(parent)
 
         # initialization
diff --git a/angrmanagement/ui/dialogs/hook.py b/angrmanagement/ui/dialogs/hook.py
index 99b0fb044..be2de46e9 100644
--- a/angrmanagement/ui/dialogs/hook.py
+++ b/angrmanagement/ui/dialogs/hook.py
@@ -3,14 +3,13 @@
 from PySide2.QtGui import QTextOption
 from pyqodeng.core.api import CodeEdit
 from pyqodeng.core.modes import CaretLineHighlighterMode, PygmentsSyntaxHighlighter, AutoIndentMode
-from ...data.instance import Instance
 
 
 class HookDialog(QDialog):
     """
     Provide templetes of hook function.
     """
-    def __init__(self, instance: Instance, addr=None, parent=None):
+    def __init__(self, instance, addr=None, parent=None):
         super().__init__(parent)
 
         # initialization
diff --git a/angrmanagement/ui/dialogs/jumpto.py b/angrmanagement/ui/dialogs/jumpto.py
index 176ee1c44..0360e85c0 100644
--- a/angrmanagement/ui/dialogs/jumpto.py
+++ b/angrmanagement/ui/dialogs/jumpto.py
@@ -39,7 +39,7 @@ def _init_widgets(self):
         address_label = QLabel(self)
         address_label.setText('Address')
 
-        address = QAddressInput(self._on_address_changed, self._disasm_view.workspace, parent=self)
+        address = QAddressInput(self._on_address_changed, self._disasm_view.instance, parent=self)
         self._address_box = address
 
         address_layout = QHBoxLayout()
diff --git a/angrmanagement/ui/dialogs/rename_node.py b/angrmanagement/ui/dialogs/rename_node.py
index 8ebcff098..db242aaa6 100644
--- a/angrmanagement/ui/dialogs/rename_node.py
+++ b/angrmanagement/ui/dialogs/rename_node.py
@@ -157,7 +157,7 @@ def _on_ok_clicked(self):
         if node_name is not None:
             if self._code_view is not None and self._node is not None:
                 # need workspace for altering callbacks of changes
-                workspace = self._code_view.workspace
+                workspace = self._code_view.instance.workspace
                 code_kb = self._code_view.codegen.kb
 
                 # stack variable
@@ -182,7 +182,7 @@ def _on_ok_clicked(self):
                         node_name
                     )
 
-                    self._code_view.workspace.instance.kb.labels[self._node.variable.addr] = node_name
+                    self._code_view.instance.kb.labels[self._node.variable.addr] = node_name
                     self._node.variable.name = node_name
                     self._node.variable.renamed = True
 
diff --git a/angrmanagement/ui/dialogs/set_comment.py b/angrmanagement/ui/dialogs/set_comment.py
index 74bfb0f57..75889e5d2 100644
--- a/angrmanagement/ui/dialogs/set_comment.py
+++ b/angrmanagement/ui/dialogs/set_comment.py
@@ -59,8 +59,8 @@ def _init_widgets(self):
 
         # comment textbox
         comment_txtbox = QCommentTextBox(textconfirmed_callback=self._on_ok_clicked, parent=self)
-        if self._comment_addr in self._workspace.instance.project.kb.comments:
-            comment_txtbox.setPlainText(self._workspace.instance.project.kb.comments[self._comment_addr])
+        if self._comment_addr in self._workspace.main_instance.project.kb.comments:
+            comment_txtbox.setPlainText(self._workspace.main_instance.project.kb.comments[self._comment_addr])
             comment_txtbox.selectAll()
         self._comment_textbox = comment_txtbox
 
diff --git a/angrmanagement/ui/main_window.py b/angrmanagement/ui/main_window.py
index 1b6ecea65..66337b0eb 100644
--- a/angrmanagement/ui/main_window.py
+++ b/angrmanagement/ui/main_window.py
@@ -162,12 +162,12 @@ def open_load_plugins_dialog(self):
         dlg.exec_()
 
     def open_newstate_dialog(self):
-        if self.workspace.instance.project.am_none:
+        if self.workspace.main_instance.project.am_none:
             QMessageBox.critical(self,
                                  "Cannot create new states",
                                  "Please open a binary to analyze first.")
             return
-        new_state_dialog = NewState(self.workspace.instance, parent=self, create_simgr=True)
+        new_state_dialog = NewState(self.workspace.main_instance, parent=self, create_simgr=True)
         new_state_dialog.exec_()
 
     def open_doc_link(self):
@@ -198,7 +198,7 @@ def _init_statusbar(self):
         def on_cancel():
             if self.workspace is None:
                 return
-            for job in self.workspace.instance.jobs:
+            for job in self.workspace.main_instance.jobs:
                 if job.blocking:
                     job.keyboard_interrupt()
                     break
@@ -253,14 +253,14 @@ def _init_workspace(self):
         self.central_widget.setDockNestingEnabled(True)
 
         def set_caption(**kwargs):  # pylint: disable=unused-argument
-            if self.workspace.instance.project.am_none:
+            if self.workspace.main_instance.project.am_none:
                 self.caption = ''
-            elif self.workspace.instance.project.filename is None:
+            elif self.workspace.main_instance.project.filename is None:
                 self.caption = "Loaded from stream"
             else:
-                self.caption = os.path.basename(self.workspace.instance.project.filename)
+                self.caption = os.path.basename(self.workspace.main_instance.project.filename)
 
-        self.workspace.instance.project.am_subscribe(set_caption)
+        self.workspace.main_instance.project.am_subscribe(set_caption)
 
         self.tab = self.central_widget.findChild(QTabBar)
         self.tab.tabBarClicked.connect(self.on_center_tab_clicked)
@@ -270,7 +270,7 @@ def set_caption(**kwargs):  # pylint: disable=unused-argument
     #
 
     def interrupt_current_job(self):
-        self.workspace.instance.interrupt_current_job()
+        self.workspace.main_instance.interrupt_current_job()
 
     def _init_shortcuts(self):
         """
@@ -405,7 +405,7 @@ def _init_url_scheme_handler(self):
     def closeEvent(self, event):
 
         # Ask if the user wants to save things
-        if self.workspace.instance is not None and not self.workspace.instance.project.am_none:
+        if self.workspace.main_instance is not None and not self.workspace.main_instance.project.am_none:
             msgbox = QMessageBox()
             msgbox.setWindowTitle("Save database")
             msgbox.setText("angr management is about to exit. Do you want to save the database?")
@@ -494,8 +494,8 @@ def open_docker_button(self):
         if img_name is None:
             return
         target = archr.targets.DockerImageTarget(img_name, target_path=None)
-        self.workspace.instance.add_job(LoadTargetJob(target))
-        self.workspace.instance.img_name = img_name
+        self.workspace.main_instance.add_job(LoadTargetJob(target))
+        self.workspace.main_instance.img_name = img_name
 
     def load_trace_file(self, file_path):
         if isurl(file_path):
@@ -536,13 +536,13 @@ def load_file(self, file_path):
                     self.workspace.load_trace_from_path(file_path)
                     return
 
-                self.workspace.instance.binary_path = file_path
-                self.workspace.instance.original_binary_path = file_path
+                self.workspace.main_instance.binary_path = file_path
+                self.workspace.main_instance.original_binary_path = file_path
                 if file_path.endswith(".adb"):
                     self._load_database(file_path)
                 else:
                     self._recent_file(file_path)
-                    self.workspace.instance.add_job(LoadBinaryJob(file_path))
+                    self.workspace.main_instance.add_job(LoadBinaryJob(file_path))
             else:
                 QMessageBox.critical(self,
                                      "File not found",
@@ -571,8 +571,8 @@ def load_file(self, file_path):
 
                 if target_path:
                     # open the file - now it's a local file
-                    self.workspace.instance.binary_path = target_path
-                    self.workspace.instance.original_binary_path = file_path
+                    self.workspace.main_instance.binary_path = target_path
+                    self.workspace.main_instance.original_binary_path = file_path
                     self.load_file(target_path)
 
     def load_database(self):
@@ -588,22 +588,22 @@ def load_database(self):
         self._load_database(file_path)
 
     def save_database(self):
-        if self.workspace.instance is None or self.workspace.instance.project.am_none:
+        if self.workspace.main_instance is None or self.workspace.main_instance.project.am_none:
             return True
 
-        if self.workspace.instance.database_path is None:
+        if self.workspace.main_instance.database_path is None:
             return self.save_database_as()
         else:
-            return self._save_database(self.workspace.instance.database_path)
+            return self._save_database(self.workspace.main_instance.database_path)
 
     def save_database_as(self):
 
-        if self.workspace.instance is None or self.workspace.instance.project.am_none:
+        if self.workspace.main_instance is None or self.workspace.main_instance.project.am_none:
             return False
 
-        default_database_path = self.workspace.instance.database_path
+        default_database_path = self.workspace.main_instance.database_path
         if default_database_path is None:
-            default_database_path = os.path.normpath(self.workspace.instance.project.filename) + ".adb"
+            default_database_path = os.path.normpath(self.workspace.main_instance.project.filename) + ".adb"
 
         # Open File window
         file_path, _ = QFileDialog.getSaveFileName(
@@ -639,14 +639,14 @@ def run_induction_variable_analysis(self):
         self.workspace._get_or_create_disassembly_view().run_induction_variable_analysis()
 
     def run_dependency_analysis(self, func_addr: Optional[int] = None, func_arg_idx: Optional[int] = None):
-        if self.workspace is None or self.workspace.instance is None:
+        if self.workspace is None or self.workspace.main_instance is None:
             return
         dep_analysis_job = DependencyAnalysisJob(func_addr=func_addr, func_arg_idx=func_arg_idx)
-        self.workspace.instance.add_job(dep_analysis_job)
+        self.workspace.main_instance.add_job(dep_analysis_job)
 
     def run_analysis(self):
         if self.workspace:
-            self.workspace.run_analysis()
+            self.workspace.main_instance.run_analysis()
 
     def decompile_current_function(self):
         if self.workspace is not None:
@@ -657,7 +657,7 @@ def view_proximity_for_current_function(self):
             self.workspace.view_proximity_for_current_function()
 
     def interact(self):
-        self.workspace.interact_program(self.workspace.instance.img_name)
+        self.workspace.interact_program(self.workspace.main_instance.img_name)
 
     #
     # Other public methods
@@ -703,7 +703,7 @@ def _load_database(self, file_path: str):
 
         job = LoadAngrDBJob(file_path, ["global", "pseudocode_variable_kb"], other_kbs=other_kbs, extra_info=extra_info)
         job._on_finish = partial(self._on_load_database_finished, job)
-        self.workspace.instance.add_job(job)
+        self.workspace.main_instance.add_job(job)
 
     def _on_load_database_finished(self, job: LoadAngrDBJob):
         proj = job.project
@@ -716,17 +716,17 @@ def _on_load_database_finished(self, job: LoadAngrDBJob):
         cfg = proj.kb.cfgs['CFGFast']
         cfb = proj.analyses.CFB()  # it will load functions from kb
 
-        self.workspace.instance.database_path = job.file_path
+        self.workspace.main_instance.database_path = job.file_path
 
-        self.workspace.instance._reset_containers()
-        self.workspace.instance.project = proj
-        self.workspace.instance.cfg = cfg
-        self.workspace.instance.cfb = cfb
+        self.workspace.main_instance._reset_containers()
+        self.workspace.main_instance.project = proj
+        self.workspace.main_instance.cfg = cfg
+        self.workspace.main_instance.cfb = cfb
         if "pseudocode_variable_kb" in job.other_kbs:
-            self.workspace.instance.pseudocode_variable_kb = job.other_kbs["pseudocode_variable_kb"]
+            self.workspace.main_instance.pseudocode_variable_kb = job.other_kbs["pseudocode_variable_kb"]
         else:
-            self.workspace.instance.initialize_pseudocode_variable_kb()
-        self.workspace.instance.project.am_event(initialized=True)
+            self.workspace.main_instance.initialize_pseudocode_variable_kb()
+        self.workspace.main_instance.project.am_event(initialized=True)
 
         # trigger callbacks
         self.workspace.reload()
@@ -734,7 +734,7 @@ def _on_load_database_finished(self, job: LoadAngrDBJob):
         self.workspace.plugins.angrdb_load_entries(job.extra_info)
 
     def _save_database(self, file_path):
-        if self.workspace.instance is None or self.workspace.instance.project.am_none:
+        if self.workspace.main_instance is None or self.workspace.main_instance.project.am_none:
             return False
 
         if AngrDB is None:
@@ -744,14 +744,14 @@ def _save_database(self, file_path):
 
         self.workspace.plugins.handle_project_save(file_path)
 
-        angrdb = AngrDB(project=self.workspace.instance.project)
+        angrdb = AngrDB(project=self.workspace.main_instance.project)
         extra_info = self.workspace.plugins.angrdb_store_entries()
         angrdb.dump(file_path, kbs=[
-                self.workspace.instance.kb,
-                self.workspace.instance.pseudocode_variable_kb,
+                self.workspace.main_instance.kb,
+                self.workspace.main_instance.pseudocode_variable_kb,
                 ],
             extra_info=extra_info,
             )
 
-        self.workspace.instance.database_path = file_path
+        self.workspace.main_instance.database_path = file_path
         return True
diff --git a/angrmanagement/ui/menus/disasm_insn_context_menu.py b/angrmanagement/ui/menus/disasm_insn_context_menu.py
index 042febde4..e6130c463 100644
--- a/angrmanagement/ui/menus/disasm_insn_context_menu.py
+++ b/angrmanagement/ui/menus/disasm_insn_context_menu.py
@@ -64,7 +64,7 @@ def _find_in_execution(self):
         self._disasm_view.refresh()
 
     def _toggle_breakpoint(self):
-        self._disasm_view.workspace.instance.breakpoint_mgr.toggle_exec_breakpoint(self.insn_addr)
+        self._disasm_view.instance.breakpoint_mgr.toggle_exec_breakpoint(self.insn_addr)
         self._disasm_view.refresh()
 
     def _add_hook(self):
diff --git a/angrmanagement/ui/toolbars/debug_toolbar.py b/angrmanagement/ui/toolbars/debug_toolbar.py
index c7d361495..daff5c580 100644
--- a/angrmanagement/ui/toolbars/debug_toolbar.py
+++ b/angrmanagement/ui/toolbars/debug_toolbar.py
@@ -16,8 +16,8 @@ class AvailableDebuggersModel(QAbstractItemModel):
 
     def __init__(self, workspace: 'Workspace'):
         super().__init__()
-        self.debugger_mgr: 'DebuggerManager' = workspace.instance.debugger_mgr
-        self.debugger_list_mgr: 'DebuggerListManager' = workspace.instance.debugger_list_mgr
+        self.debugger_mgr: 'DebuggerManager' = workspace.main_instance.debugger_mgr
+        self.debugger_list_mgr: 'DebuggerListManager' = workspace.main_instance.debugger_list_mgr
         self.last_str = {}
 
     def rowCount(self, parent):  # pylint:disable=unused-argument
@@ -50,11 +50,12 @@ def index_to_debugger(self, index: int) -> Optional['Debugger']:
 class DebugToolbar(Toolbar):
     """
     Debugger Control Toolbar
+    TODO: decouple this from MainWindow and workspace.instance (main_instance)
     """
     def __init__(self, main_window: 'MainWindow'):
         super().__init__(main_window, 'DebugToolbar')
         self.workspace: 'Workspace' = main_window.workspace
-        self.instance: 'Instance' = self.workspace.instance
+        self.instance: 'Instance' = self.workspace.main_instance
 
         self._cont_backward_act = ToolbarAction(qta.icon("fa5s.fast-backward", color=Conf.palette_buttontext),
             'Continue-Backward', 'Reverse-Continue', self._on_cont_backward)
diff --git a/angrmanagement/ui/views/breakpoints_view.py b/angrmanagement/ui/views/breakpoints_view.py
index ca975703b..9178a3a2e 100644
--- a/angrmanagement/ui/views/breakpoints_view.py
+++ b/angrmanagement/ui/views/breakpoints_view.py
@@ -145,8 +145,8 @@ class BreakpointsView(BaseView):
     Breakpoints table view.
     """
 
-    def __init__(self, workspace, default_docking_position, *args, **kwargs):
-        super().__init__('breakpoints', workspace, default_docking_position, *args, **kwargs)
+    def __init__(self, instance, default_docking_position, *args, **kwargs):
+        super().__init__('breakpoints', instance, default_docking_position, *args, **kwargs)
         self.base_caption = 'Breakpoints'
         self._tbl_widget: Optional[QBreakpointTableWidget] = None
         self._init_widgets()
@@ -161,6 +161,6 @@ def minimumSizeHint(*args, **kwargs):  # pylint:disable=unused-argument
 
     def _init_widgets(self):
         vlayout = QVBoxLayout()
-        self._tbl_widget = QBreakpointTableWidget(self.workspace.instance.breakpoint_mgr, self.workspace)
+        self._tbl_widget = QBreakpointTableWidget(self.instance.breakpoint_mgr, self.instance.workspace)
         vlayout.addWidget(self._tbl_widget)
         self.setLayout(vlayout)
diff --git a/angrmanagement/ui/views/call_explorer_view.py b/angrmanagement/ui/views/call_explorer_view.py
index d730c319b..ff68e7222 100644
--- a/angrmanagement/ui/views/call_explorer_view.py
+++ b/angrmanagement/ui/views/call_explorer_view.py
@@ -59,8 +59,8 @@ class CallExplorerView(BaseView):
     Call Explorer view.
     """
 
-    def __init__(self, workspace, default_docking_position, *args, **kwargs):
-        super().__init__('call_explorer', workspace, default_docking_position, *args, **kwargs)
+    def __init__(self, instance, default_docking_position, *args, **kwargs):
+        super().__init__('call_explorer', instance, default_docking_position, *args, **kwargs)
 
         self._last_updated_func: Optional[Union[int, Function]] = None
         self._inhibit_update: bool = False
@@ -74,7 +74,7 @@ def __init__(self, workspace, default_docking_position, *args, **kwargs):
         self.height_hint = 400
         self.updateGeometry()
 
-        self._dbg_manager = workspace.instance.debugger_mgr
+        self._dbg_manager = instance.debugger_mgr
         self._dbg_watcher = DebuggerWatcher(self._on_debugger_state_updated, self._dbg_manager.debugger)
         self._on_debugger_state_updated()
 
@@ -118,7 +118,7 @@ def _on_item_clicked(self, index):
         self._inhibit_update = True
 
         # Replay up to just before call
-        dbg = self.workspace.instance.debugger_mgr.debugger
+        dbg = self.instance.debugger_mgr.debugger
         dbg.replay_to_event(dbg._btrace.get_prev_exec_event(item.event, vcpu=dbg._trace_dbg.vcpu))
 
         self._inhibit_update = original_inhibit
@@ -130,7 +130,7 @@ def _on_item_double_clicked(self, index):
         item = self._model.itemFromIndex(index)
         # Replay after the jump, jumping into the called function
         # FIXME: Doesn't consider proper selected debugger, assumes bintrace
-        dbg = self.workspace.instance.debugger_mgr.debugger
+        dbg = self.instance.debugger_mgr.debugger
         dbg.replay_to_event(dbg._btrace.get_next_exec_event(item.event, vcpu=dbg._trace_dbg.vcpu))
 
     def _on_item_expanded(self, index):
@@ -139,7 +139,7 @@ def _on_item_expanded(self, index):
         """
         expanding_item = self._model.itemFromIndex(index)
         if not expanding_item.populated:
-            dbg = self.workspace.instance.debugger_mgr.debugger
+            dbg = self.instance.debugger_mgr.debugger
             if dbg.am_none:
                 return
             called = dbg.get_called_functions(expanding_item.event)
diff --git a/angrmanagement/ui/views/code_view.py b/angrmanagement/ui/views/code_view.py
index f74ad3940..435266818 100644
--- a/angrmanagement/ui/views/code_view.py
+++ b/angrmanagement/ui/views/code_view.py
@@ -30,8 +30,8 @@ class CodeView(BaseView):
 
     FUNCTION_SPECIFIC_VIEW = True
 
-    def __init__(self, workspace, default_docking_position, *args, **kwargs):
-        super().__init__('pseudocode', workspace, default_docking_position, *args, **kwargs)
+    def __init__(self, instance, default_docking_position, *args, **kwargs):
+        super().__init__('pseudocode', instance, default_docking_position, *args, **kwargs)
 
         self.base_caption = 'Pseudocode'
 
@@ -95,7 +95,7 @@ def function(self, v):
     #
 
     def reload(self):
-        if self.workspace.instance.project.am_none:
+        if self.instance.project.am_none:
             return
         self._options.reload(force=True)
         self.vars_must_struct = set()
@@ -111,18 +111,18 @@ def decompile(self, clear_prototype: bool=True, focus=False, focus_addr=None, fl
             self._function.ran_cca = False
 
         if reset_cache:
-            self.workspace.instance.kb.structured_code.discard((self._function.addr, flavor))
-            variables = self.workspace.instance.pseudocode_variable_kb.variables
+            self.instance.kb.structured_code.discard((self._function.addr, flavor))
+            variables = self.instance.pseudocode_variable_kb.variables
             if variables.has_function_manager(self._function.addr):
                 del variables[self._function.addr]
 
         def decomp_ready():
             # this code is _partially_ duplicated from _on_new_function. be careful!
-            available = self.workspace.instance.kb.structured_code.available_flavors(self._function.addr)
+            available = self.instance.kb.structured_code.available_flavors(self._function.addr)
             self._update_available_views(available)
             if available:
                 chosen_flavor = flavor if flavor in available else available[0]
-                self.codegen.am_obj = self.workspace.instance.kb.structured_code[(self._function.addr,
+                self.codegen.am_obj = self.instance.kb.structured_code[(self._function.addr,
                                                                                   chosen_flavor)].codegen
                 self.codegen.am_event(already_regenerated=True)
                 self._focus_core(focus, focus_addr)
@@ -134,7 +134,7 @@ def decomp_ready():
         def decomp():
             job = DecompileFunctionJob(
                 self._function.am_obj,
-                cfg=self.workspace.instance.cfg,
+                cfg=self.instance.cfg,
                 options=self._options.option_and_values,
                 optimization_passes=self._options.selected_passes,
                 peephole_optimizations=self._options.selected_peephole_opts,
@@ -143,17 +143,17 @@ def decomp():
                 blocking=True,
                 regen_clinic=regen_clinic,
             )
-            self.workspace.instance.add_job(job)
+            self.instance.add_job(job)
 
         if self._function.ran_cca is False:
             # run calling convention analysis for this function
             varrec_job = VariableRecoveryJob(
-                **self.workspace._analysis_configuration['varec'].to_dict(),
+                **self.instance._analysis_configuration['varec'].to_dict(),
                 workers=0,
                 on_finish=decomp,
                 func_addr=self._function.addr
             )
-            self.workspace.instance.add_job(varrec_job)
+            self.instance.add_job(varrec_job)
         else:
             decomp()
 
@@ -190,10 +190,10 @@ def _on_new_addr(self, already_moved=False, focus=False, **kwargs):  # pylint: d
                 self._focus_core(True, None)
         else:
             # try to find the right function
-            block_addr, _ = self.workspace.instance.cfb.floor_item(self.addr.am_obj)
-            block = self.workspace.instance.cfg.get_any_node(block_addr)
+            block_addr, _ = self.instance.cfb.floor_item(self.addr.am_obj)
+            block = self.instance.cfg.get_any_node(block_addr)
             if block is not None:
-                func = self.workspace.instance.kb.functions[block.function_address]
+                func = self.instance.kb.functions[block.function_address]
                 if func is not self._function.am_obj:
                     self._function.am_obj = func
                     self._function.am_event(focus_addr=self.addr.am_obj, focus=focus)
@@ -242,12 +242,12 @@ def _on_codegen_changes(self, already_regenerated=False, event: Optional[str]=No
             pass
         else:
             if event == "retype_variable":
-                dec = self.workspace.instance.project.analyses.Decompiler(
+                dec = self.instance.project.analyses.Decompiler(
                     self._function,
-                    variable_kb=self.workspace.instance.pseudocode_variable_kb,
+                    variable_kb=self.instance.pseudocode_variable_kb,
                     decompile=False
                 )
-                dec_cache = self.workspace.instance.kb.structured_code[(self._function.addr, 'pseudocode')]
+                dec_cache = self.instance.kb.structured_code[(self._function.addr, 'pseudocode')]
                 new_codegen = dec.reflow_variable_types(
                     dec_cache.type_constraints,
                     dec_cache.var_to_typevar or {},
@@ -301,12 +301,12 @@ def _on_new_function(self, focus=False, focus_addr=None, flavor=None, **kwargs):
         if not self.codegen.am_none and self._last_function is self._function.am_obj:
             self._focus_core(focus, focus_addr)
             return
-        available = self.workspace.instance.kb.structured_code.available_flavors(self._function.addr)
+        available = self.instance.kb.structured_code.available_flavors(self._function.addr)
         self._update_available_views(available)
         should_decompile = True
         if available:
             chosen_flavor = flavor if flavor in available else available[0]
-            cached = self.workspace.instance.kb.structured_code[(self._function.addr, chosen_flavor)].codegen
+            cached = self.instance.kb.structured_code[(self._function.addr, chosen_flavor)].codegen
             if not isinstance(cached, DummyStructuredCodeGenerator):
                 should_decompile = False
                 self.codegen.am_obj = cached
@@ -348,11 +348,11 @@ def _on_mouse_doubleclicked(self):
                 if selected_node.callee_func is not None:
                     self.jump_history.record_address(selected_node.tags['ins_addr'])
                     self.jump_history.jump_to(selected_node.callee_func.addr)
-                    self.workspace.decompile_function(selected_node.callee_func, view=self)
+                    self.instance.workspace.decompile_function(selected_node.callee_func, view=self)
             elif isinstance(selected_node, CConstant):
                 # jump to highlighted constants
                 if selected_node.reference_values is not None and selected_node.value is not None:
-                    self.workspace.jump_to(selected_node.value)
+                    self.instance.workspace.jump_to(selected_node.value)
 
     def _jump_to(self, addr:int):
         self.addr.am_obj = addr
@@ -379,7 +379,7 @@ def keyPressEvent(self, event):
         key = event.key()
         if key == Qt.Key_Tab:
             # Switch back to disassembly view
-            self.workspace.jump_to(self.addr.am_obj)
+            self.instance.workspace.jump_to(self.addr.am_obj)
             return True
         elif key == Qt.Key_Escape:
             self.jump_back()
@@ -387,10 +387,10 @@ def keyPressEvent(self, event):
         elif key == Qt.Key_Space:
             if not self.codegen.am_none:
                 flavor = self.codegen.flavor
-                flavors = self.workspace.instance.kb.structured_code.available_flavors(self._function.addr)
+                flavors = self.instance.kb.structured_code.available_flavors(self._function.addr)
                 idx = flavors.index(flavor)
                 newidx = (idx + 1) % len(flavors)
-                self.codegen.am_obj = self.workspace.instance.kb.structured_code[(self._function.addr,
+                self.codegen.am_obj = self.instance.kb.structured_code[(self._function.addr,
                                                                                   flavors[newidx])].codegen
                 self.codegen.am_event()
                 return True
@@ -406,7 +406,7 @@ def _update_available_views(self, available):
     def _on_view_selector_changed(self, index):
         if not self._function.am_none:
             key = (self._function.addr, self._view_selector.itemText(index))
-            self.codegen.am_obj = self.workspace.instance.kb.structured_code[key].codegen
+            self.codegen.am_obj = self.instance.kb.structured_code[key].codegen
             self.codegen.am_event()
 
     #
@@ -427,7 +427,7 @@ def _init_widgets(self):
         textedit_dock.setWidget(self._textedit)
 
         # decompilation
-        self._options = QDecompilationOptions(self, self.workspace.instance)
+        self._options = QDecompilationOptions(self, self.instance)
         options_dock = QDockWidget('Decompilation Options', self._options)
         window.addDockWidget(Qt.RightDockWidgetArea, options_dock)
         options_dock.setWidget(self._options)
@@ -465,4 +465,4 @@ def _init_widgets(self):
 
         self._textedit.focusWidget()
 
-        self.workspace.plugins.instrument_code_view(self)
+        self.instance.workspace.plugins.instrument_code_view(self)
diff --git a/angrmanagement/ui/views/console_view.py b/angrmanagement/ui/views/console_view.py
index f4496583e..e02d1b495 100644
--- a/angrmanagement/ui/views/console_view.py
+++ b/angrmanagement/ui/views/console_view.py
@@ -14,8 +14,8 @@ class ConsoleView(BaseView):
     Console view providing IPython interactive session.
     """
 
-    def __init__(self, workspace, default_docking_position, *args, **kwargs):
-        super().__init__('console', workspace, default_docking_position, *args, **kwargs)
+    def __init__(self, instance, default_docking_position, *args, **kwargs):
+        super().__init__('console', instance, default_docking_position, *args, **kwargs)
 
         self.base_caption = 'Console'
         self._ipython_widget = None
@@ -37,9 +37,9 @@ def reload(self):
         namespace = {'angr': angr,
                      'claripy': claripy,
                      'cle': cle,
-                     'workspace': self.workspace,
-                     'instance': self.workspace.instance,
-                     'project': self.workspace.instance.project,
+                     'workspace': self.instance.workspace,
+                     'instance': self.instance,
+                     'project': self.instance.project,
                      }
         self._ipython_widget.push_namespace(namespace)
 
@@ -92,6 +92,6 @@ def _init_widgets(self):
 
     def command_executed(self,msg):
         if msg["msg_type"] == "execute_reply" and msg["content"]["status"] == "ok":
-            view = self.workspace.view_manager.first_view_in_category("disassembly")
+            view = self.instance.workspace.view_manager.first_view_in_category("disassembly")
             if view is not None:
                 view.refresh()
diff --git a/angrmanagement/ui/views/data_dep_view.py b/angrmanagement/ui/views/data_dep_view.py
index adcf80492..84a4c8e4b 100644
--- a/angrmanagement/ui/views/data_dep_view.py
+++ b/angrmanagement/ui/views/data_dep_view.py
@@ -29,15 +29,14 @@ def function(self):
 
     FUNCTION_SPECIFIC_VIEW = False
 
-    def __init__(self, workspace, default_docking_position, *args, **kwargs):
-        super().__init__('data_dependency', workspace, default_docking_position, *args, **kwargs)
+    def __init__(self, instance, default_docking_position, *args, **kwargs):
+        super().__init__('data_dependency', instance, default_docking_position, *args, **kwargs)
 
         self.base_caption = 'Data Dependency'
-        self.workspace = workspace
 
         # Get all instructions in the program
         self._instructions: Dict[int, 'CsInsn'] = {}
-        inst = self.workspace.instance
+        inst = self.instance
         for _, func in inst.kb.functions.items():
             for block in func.blocks:
                 disass = block.disassembly
@@ -121,7 +120,7 @@ def analysis_params(self, new_params: dict):
         #     _l.error("Unable to generate data dependency graph with provided parameters!")
 
     def run_analysis(self):
-        inst = self.workspace.instance
+        inst = self.instance
 
         data_dep: 'DataDependencyGraphAnalysis' = inst.project.analyses.DataDep(
             self._end_state,
diff --git a/angrmanagement/ui/views/dep_view.py b/angrmanagement/ui/views/dep_view.py
index b69baa083..14fd094bb 100644
--- a/angrmanagement/ui/views/dep_view.py
+++ b/angrmanagement/ui/views/dep_view.py
@@ -20,11 +20,10 @@ class DependencyView(BaseView):
     Creates view for dependency analysis.
     """
 
-    def __init__(self, workspace, default_docking_position, *args, **kwargs):
-        super().__init__('dependencies', workspace, default_docking_position, *args, **kwargs)
+    def __init__(self, instance, default_docking_position, *args, **kwargs):
+        super().__init__('dependencies', instance, default_docking_position, *args, **kwargs)
 
         self.base_caption = 'Dependencies'
-        self.workspace = workspace
 
         # UI widgets
         self._graph_widget: QDependencyGraph = None
@@ -75,7 +74,7 @@ def sizeHint(self):
 
     def _init_widgets(self):
 
-        self._graph_widget = QDependencyGraph(self.workspace, self)
+        self._graph_widget = QDependencyGraph(self.instance, self)
 
         hlayout = QHBoxLayout()
         hlayout.addWidget(self._graph_widget)
@@ -84,7 +83,7 @@ def _init_widgets(self):
         self.setLayout(hlayout)
 
     def _register_events(self):
-        self.workspace.current_screen.am_subscribe(self.on_screen_changed)
+        self.instance.workspace.current_screen.am_subscribe(self.on_screen_changed)
 
     def _convert_node(self, node: Definition, converted: Dict[Definition,QDepGraphBlock]) -> Optional[QDepGraphBlock]:
         if node in converted:
@@ -94,8 +93,8 @@ def _convert_node(self, node: Definition, converted: Dict[Definition,QDepGraphBl
         if isinstance(node.codeloc, ExternalCodeLocation):
             return None
 
-        if self.workspace.instance.project.is_hooked(node.codeloc.block_addr):
-            hook = self.workspace.instance.project.hooked_by(node.codeloc.block_addr)
+        if self.instance.project.is_hooked(node.codeloc.block_addr):
+            hook = self.instance.project.hooked_by(node.codeloc.block_addr)
             if isinstance(hook, (SIM_PROCEDURES['stubs']['UnresolvableJumpTarget'],
                                  SIM_PROCEDURES['stubs']['UnresolvableCallTarget'])):
                 return None
diff --git a/angrmanagement/ui/views/disassembly_view.py b/angrmanagement/ui/views/disassembly_view.py
index f36900490..5aa6357bc 100644
--- a/angrmanagement/ui/views/disassembly_view.py
+++ b/angrmanagement/ui/views/disassembly_view.py
@@ -51,8 +51,8 @@ class DisassemblyView(SynchronizedView):
     view_visibility_changed = Signal()
     disassembly_level_changed = Signal(DisassemblyLevel)
 
-    def __init__(self, workspace, *args, **kwargs):
-        super().__init__('disassembly', workspace, *args, **kwargs)
+    def __init__(self, instance, *args, **kwargs):
+        super().__init__('disassembly', instance, *args, **kwargs)
 
         self.base_caption = 'Disassembly'
         self._disassembly_level = DisassemblyLevel.MachineCode
@@ -201,31 +201,31 @@ def function(self, v):
     # Callbacks
     #
 
-    # All callbacks are proxies to self.workspace.instance. These properties *in this class* may be removed in the near
+    # All callbacks are proxies to self.instance. These properties *in this class* may be removed in the near
     # future.
     @property
     def insn_backcolor_callback(self):
-        return self.workspace.instance.insn_backcolor_callback
+        return self.instance.insn_backcolor_callback
 
     @insn_backcolor_callback.setter
     def insn_backcolor_callback(self, v):
-        self.workspace.instance.insn_backcolor_callback = v
+        self.instance.insn_backcolor_callback = v
 
     @property
     def label_rename_callback(self):
-        return self.workspace.instance.label_rename_callback
+        return self.instance.label_rename_callback
 
     @label_rename_callback.setter
     def label_rename_callback(self, v):
-        self.workspace.instance.label_rename_callback = v
+        self.instance.label_rename_callback = v
 
     @property
     def set_comment_callback(self):
-        return self.workspace.instance.set_comment_callback
+        return self.instance.set_comment_callback
 
     @set_comment_callback.setter
     def set_comment_callback(self, v):
-        self.workspace.instance.set_comment_callback = v
+        self.instance.set_comment_callback = v
 
     def on_variable_recovered(self, func_addr: int):
         if not self._current_function.am_none and self._current_function.addr == func_addr:
@@ -310,7 +310,7 @@ def instruction_context_menu(self, insn, pos):
         # pass in the instruction address
         self._insn_menu.insn_addr = insn.addr
         # pop up the menu
-        mnu = self._insn_menu.qmenu(extra_entries=list(self.workspace.plugins.build_context_menu_insn(insn)),
+        mnu = self._insn_menu.qmenu(extra_entries=list(self.instance.workspace.plugins.build_context_menu_insn(insn)),
                                     cached=False)
         self.append_view_menu_actions(mnu)
         mnu.exec_(pos)
@@ -377,7 +377,7 @@ def popup_comment_dialog(self):
         if comment_addr is None:
             return
 
-        dialog = SetComment(self.workspace, comment_addr, parent=self)
+        dialog = SetComment(self.instance.workspace, comment_addr, parent=self)
         dialog.exec_()
 
     def popup_newstate_dialog(self, async_=True):
@@ -385,7 +385,7 @@ def popup_newstate_dialog(self, async_=True):
         if addr is None:
             return
 
-        dialog = NewState(self.workspace.instance, addr=addr, create_simgr=True, parent=self)
+        dialog = NewState(self.instance, addr=addr, create_simgr=True, parent=self)
         if async_:
             dialog.show()
         else:
@@ -397,7 +397,7 @@ def popup_hook_dialog(self, async_=True, addr=None):
         if addr is None:
             return
 
-        dialog = HookDialog(self.workspace.instance, addr=addr, parent=self)
+        dialog = HookDialog(self.instance, addr=addr, parent=self)
         if async_:
             dialog.show()
         else:
@@ -427,7 +427,7 @@ def popup_func_doc_dialog(self, instr_addr):
                 url = "http://"
                 ftype = "<>"
                 doc_tuple = (doc_string, url, ftype)
-            dialog = FuncDocDialog(self.workspace.instance,
+            dialog = FuncDocDialog(self.instance,
                                    addr=target, name=operand.text, doc_tuple=doc_tuple,
                                    parent=self)
             dialog.show()
@@ -458,7 +458,7 @@ def popup_dependson_dialog(self, addr: Optional[int]=None, use_operand=False, fu
         if func:
             # attempt to pass in a function
             try:
-                the_func = self.workspace.instance.kb.functions.get_by_addr(addr)
+                the_func = self.instance.kb.functions.get_by_addr(addr)
             except KeyError:
                 the_func = None
         else:
@@ -470,7 +470,7 @@ def popup_dependson_dialog(self, addr: Optional[int]=None, use_operand=False, fu
         if dependson.location is not None:
             if dependson.arg is not None:
                 # track function argument
-                self.workspace._main_window.run_dependency_analysis(
+                self.instance.workspace._main_window.run_dependency_analysis(
                     func_addr=addr,
                     func_arg_idx=dependson.arg,
                 )
@@ -491,10 +491,10 @@ def popup_xref_dialog(self, addr=None, variable=None, dst_addr=None, async_=True
 
         if variable is not None:
             dialog = XRef(addr=addr, variable_manager=self.variable_manager, variable=variable,
-                          instance=self.workspace.instance, parent=self)
+                          instance=self.instance, parent=self)
         else:
-            dialog = XRef(addr=addr, xrefs_manager=self.workspace.instance.project.kb.xrefs, dst_addr=dst_addr,
-                          instance=self.workspace.instance, parent=self)
+            dialog = XRef(addr=addr, xrefs_manager=self.instance.project.kb.xrefs, dst_addr=dst_addr,
+                          instance=self.instance, parent=self)
         if async_:
             dialog.show()
         else:
@@ -547,11 +547,11 @@ def display_linear_viewer(self, prefer=True):
         self._linear_viewer.refresh()
 
     def display_function(self, function):
-        if function.addr not in self.workspace.instance.kb.variables.function_managers:
+        if function.addr not in self.instance.kb.variables.function_managers:
             # variable information is not available
-            if self.workspace.variable_recovery_job is not None:
+            if self.instance.variable_recovery_job is not None:
                 # prioritize the analysis of this function
-                self.workspace.variable_recovery_job.prioritize_function(function.addr)
+                self.instance.variable_recovery_job.prioritize_function(function.addr)
         self.jump_history.jump_to(function.addr)
         self._display_function(function)
 
@@ -563,7 +563,7 @@ def decompile_current_function(self):
             except StopIteration:
                 curr_ins = None
 
-            self.workspace.decompile_function(self._current_function.am_obj, curr_ins=curr_ins)
+            self.instance.workspace.decompile_function(self._current_function.am_obj, curr_ins=curr_ins)
 
     def toggle_show_minimap(self, show:bool):
         """
@@ -683,43 +683,43 @@ def rename_label(self, addr, new_name):
                 kb.labels[addr] = new_name
 
             # callback first
-            if self.workspace.instance.label_rename_callback:
-                self.workspace.instance.label_rename_callback(addr=addr, new_name=new_name)
+            if self.instance.label_rename_callback:
+                self.instance.label_rename_callback(addr=addr, new_name=new_name)
 
             # redraw the current block
             self._flow_graph.update_label(addr, is_renaming=is_renaming)
 
     def avoid_addr_in_exec(self, addr):
-        self.workspace._get_or_create_symexec_view().avoid_addr_in_exec(addr)
+        self.instance.workspace._get_or_create_symexec_view().avoid_addr_in_exec(addr)
 
     def find_addr_in_exec(self, addr):
-        self.workspace._get_or_create_symexec_view().find_addr_in_exec(addr)
+        self.instance.workspace._get_or_create_symexec_view().find_addr_in_exec(addr)
 
     def run_induction_variable_analysis(self):
         if self._flow_graph.induction_variable_analysis:
             self._flow_graph.induction_variable_analysis = None
         else:
-            analyses = self.workspace.instance.project.analyses
+            analyses = self.instance.project.analyses
             ana = analyses.AffineRelationAnalysis(self._flow_graph._function_graph.function)
             self._flow_graph.induction_variable_analysis = ana
         self._flow_graph.refresh()
 
     def fetch_qblock_annotations(self, qblock):
         addr_to_annotations = defaultdict(list)
-        for annotations in self.workspace.plugins.build_qblock_annotations(qblock):
+        for annotations in self.instance.workspace.plugins.build_qblock_annotations(qblock):
             addr_to_annotations[annotations.addr].append(annotations)
         for addr in qblock.addr_to_insns.keys():
-            if addr in self.workspace.instance.project._sim_procedures:
+            if addr in self.instance.project._sim_procedures:
                 hook_annotation = QHookAnnotation(addr)
                 addr_to_annotations[addr].append(hook_annotation)
-            view = self.workspace.view_manager.first_view_in_category("symexec")
+            view = self.instance.workspace.view_manager.first_view_in_category("symexec")
             if view is not None:
                 qsimgrs = view._simgrs
                 if addr in qsimgrs.find_addrs:
                     addr_to_annotations[addr].append(QFindAddrAnnotation(addr, qsimgrs))
                 if addr in qsimgrs.avoid_addrs:
                     addr_to_annotations[addr].append(QAvoidAddrAnnotation(addr, qsimgrs))
-                for bp in self.workspace.instance.breakpoint_mgr.get_breakpoints_at(addr):
+                for bp in self.instance.breakpoint_mgr.get_breakpoints_at(addr):
                     addr_to_annotations[addr].append(QBreakAnnotation(bp))
 
         return QBlockAnnotations(addr_to_annotations, parent=qblock, disasm_view=self)
@@ -740,8 +740,8 @@ def update_highlight_regions_for_synchronized_views(self, **kwargs):  # pylint:
     #
 
     def _init_widgets(self):
-        self._linear_viewer = QLinearDisassembly(self.workspace, self, parent=self)
-        self._flow_graph = QDisassemblyGraph(self.workspace, self, parent=self)
+        self._linear_viewer = QLinearDisassembly(self.instance, self, parent=self)
+        self._flow_graph = QDisassemblyGraph(self.instance, self, parent=self)
         self._feature_map = QFeatureMap(self, parent=self)
         self._statusbar = QDisasmStatusBar(self, parent=self)
 
@@ -766,7 +766,7 @@ def _init_widgets(self):
         self.display_disasm_graph()
         # self.display_linear_viewer()
 
-        self.workspace.plugins.instrument_disassembly_view(self)
+        self.instance.workspace.plugins.instrument_disassembly_view(self)
 
     def _init_menus(self):
         self._insn_menu = DisasmInsnContextMenu(self)
@@ -783,8 +783,8 @@ def _register_events(self):
         self.infodock.selected_labels.am_subscribe(self.redraw_current_graph)
         self.infodock.qblock_code_obj_selection_changed.connect(self.redraw_current_graph)
         self._feature_map.addr.am_subscribe(lambda: self._jump_to(self._feature_map.addr.am_obj))
-        self.workspace.current_screen.am_subscribe(self.on_screen_changed)
-        self.workspace.instance.breakpoint_mgr.breakpoints.am_subscribe(lambda **kwargs: self.refresh())
+        self.instance.workspace.current_screen.am_subscribe(self.on_screen_changed)
+        self.instance.breakpoint_mgr.breakpoints.am_subscribe(lambda **kwargs: self.refresh())
 
     #
     # Private methods
@@ -800,7 +800,7 @@ def _display_function(self, the_func):
         self._statusbar.function = the_func
 
         # variable recovery
-        variable_manager = self.workspace.instance.project.kb.variables
+        variable_manager = self.instance.project.kb.variables
         self.variable_manager = variable_manager
         self.infodock.variable_manager = variable_manager
 
@@ -817,7 +817,7 @@ def _display_function(self, the_func):
         elif self._linear_viewer.isVisible() or self._t_linear_viewer_visible:
             self._linear_viewer.navigate_to_addr(the_func.addr)
 
-        view = self.workspace.view_manager.first_view_in_category('console')
+        view = self.instance.workspace.view_manager.first_view_in_category('console')
         if view is not None:
             view.push_namespace({
                 'func': the_func,
@@ -829,7 +829,7 @@ def _jump_to(self, addr, use_animation=False):
             self.display_disasm_graph(prefer=False)
 
         if self.current_graph is not self._linear_viewer:
-            function = locate_function(self.workspace.instance, addr)
+            function = locate_function(self.instance, addr)
             if function is not None:
                 self._display_function(function)
                 instr_addr = function.addr_to_instruction_addr(addr)
@@ -842,7 +842,7 @@ def _jump_to(self, addr, use_animation=False):
             self.display_linear_viewer(prefer=False)
 
         try:
-            item = self.workspace.instance.cfb.floor_item(addr)
+            item = self.instance.cfb.floor_item(addr)
             _, item = item
             if isinstance(item, MemoryData) and addr < (item.addr + item.size):
                 self.infodock.select_label(item.addr)
@@ -871,7 +871,7 @@ def _address_in_selection(self):
             return None
 
     def _get_instruction_size(self, addr: int) -> Optional[int]:
-        kb = self.workspace.instance.project.kb
+        kb = self.instance.project.kb
         f = kb.functions.floor_func(addr)
         if f is None:
             return None
diff --git a/angrmanagement/ui/views/functions_view.py b/angrmanagement/ui/views/functions_view.py
index 82e8b56a1..25186b62e 100644
--- a/angrmanagement/ui/views/functions_view.py
+++ b/angrmanagement/ui/views/functions_view.py
@@ -6,14 +6,14 @@
 
 
 class FunctionsView(BaseView):
-    def __init__(self, workspace, default_docking_position, *args, **kwargs):
-        super(FunctionsView, self).__init__('functions', workspace, default_docking_position, *args, **kwargs)
+    def __init__(self, instance, default_docking_position, *args, **kwargs):
+        super(FunctionsView, self).__init__('functions', instance, default_docking_position, *args, **kwargs)
 
         self.base_caption = 'Functions'
         self._function_table = None  # type: QFunctionTable
         self._status_label = None
 
-        self.workspace.instance.cfg.am_subscribe(self.reload)
+        self.instance.cfg.am_subscribe(self.reload)
 
         self._init_widgets()
 
@@ -42,8 +42,8 @@ def set_displayed_function_count(self, count):
         self._refresh_status_label()
 
     def reload(self):
-        if not self.workspace.instance.cfg.am_none:
-            self._function_table.function_manager = self.workspace.instance.kb.functions
+        if not self.instance.cfg.am_none:
+            self._function_table.function_manager = self.instance.kb.functions
 
     def subscribe_func_select(self, callback):
         """
@@ -58,7 +58,7 @@ def subscribe_func_select(self, callback):
     #
 
     def _init_widgets(self):
-        self._function_table = QFunctionTable(self, self.workspace, selection_callback=self._on_function_selected)
+        self._function_table = QFunctionTable(self, self.instance, selection_callback=self._on_function_selected)
         self._status_label = QLabel()
 
         vlayout = QVBoxLayout()
@@ -74,7 +74,7 @@ def _on_function_selected(self, func):
         :param function:
         :return:
         """
-        self.workspace.on_function_selected(func=func)
+        self.instance.on_function_selected(func=func)
 
     def _refresh_status_label(self):
         if self._status_label is not None:
diff --git a/angrmanagement/ui/views/hex_view.py b/angrmanagement/ui/views/hex_view.py
index d81744386..621439cdc 100644
--- a/angrmanagement/ui/views/hex_view.py
+++ b/angrmanagement/ui/views/hex_view.py
@@ -96,7 +96,7 @@ def remove(self):
         """
         Remove this breakpoint.
         """
-        self.view.workspace.instance.breakpoint_mgr.remove_breakpoint(self.bp)
+        self.view.instance.breakpoint_mgr.remove_breakpoint(self.bp)
 
     def get_tooltip(self) -> Optional[str]:
         """
@@ -162,7 +162,7 @@ def revert(self):
         """
         Revert this patch.
         """
-        pm = self.view.workspace.instance.patches
+        pm = self.view.instance.patches
         pm.remove_patch(self.patch.addr)
         pm.am_event()
 
@@ -182,7 +182,7 @@ def split(self):
             o = cursor - self.patch.addr
             new_patch = Patch(cursor, self.patch.new_bytes[o:], self.patch.comment)
             self.patch.new_bytes = self.patch.new_bytes[0:o]
-            pm = self.view.workspace.instance.patches
+            pm = self.view.instance.patches
             pm.add_patch_obj(new_patch)
             pm.am_event()
 
@@ -198,7 +198,7 @@ def merge_with(self, other: 'PatchHighlightRegion'):
         """
         if self.can_merge_with(other):
             self.patch.new_bytes += other.patch.new_bytes
-            pm = self.view.workspace.instance.patches
+            pm = self.view.instance.patches
             pm.remove_patch(other.patch.addr)
             pm.am_event()
 
@@ -210,7 +210,7 @@ def comment(self):
         dlg.exec_()
         if dlg.result:
             self.patch.comment = dlg.result
-            pm = self.view.workspace.instance.patches
+            pm = self.view.instance.patches
             pm.am_event()
 
 
@@ -1295,8 +1295,8 @@ class HexView(SynchronizedView):
 
     _widgets_initialized: bool = False
 
-    def __init__(self, workspace, default_docking_position, *args, **kwargs):
-        super().__init__('hex', workspace, default_docking_position, *args, **kwargs)
+    def __init__(self, instance, default_docking_position, *args, **kwargs):
+        super().__init__('hex', instance, default_docking_position, *args, **kwargs)
         self.base_caption: str = 'Hex'
         self.smart_highlighting_enabled: bool = True
         self._clipboard = None
@@ -1307,14 +1307,14 @@ def __init__(self, workspace, default_docking_position, *args, **kwargs):
         self._breakpoint_highlights: Sequence[BreakpointHighlightRegion] = []
 
         self._init_widgets()
-        self.workspace.instance.cfb.am_subscribe(self._reload_data)
-        self.workspace.instance.patches.am_subscribe(self._update_highlight_regions_from_patches)
-        self.workspace.instance.breakpoint_mgr.breakpoints.am_subscribe(self._update_highlight_regions_from_breakpoints)
+        self.instance.cfb.am_subscribe(self._reload_data)
+        self.instance.patches.am_subscribe(self._update_highlight_regions_from_patches)
+        self.instance.breakpoint_mgr.breakpoints.am_subscribe(self._update_highlight_regions_from_breakpoints)
         self._data_cache = {}
 
         self._reload_data()
 
-        self._dbg_manager = self.workspace.instance.debugger_mgr
+        self._dbg_manager = self.instance.debugger_mgr
         self._dbg_watcher = DebuggerWatcher(self._on_debugger_state_updated, self._dbg_manager.debugger)
         self._on_debugger_state_updated()
 
@@ -1341,10 +1341,10 @@ def _reload_data(self):
         cursor = self.inner_widget.hex.cursor
         source = self._data_source_combo.currentData()
         if source == HexDataSource.Loader:
-            if self.workspace.instance.cfb.am_none:
+            if self.instance.cfb.am_none:
                 self.inner_widget.clear()
                 return
-            loader = self.workspace.instance.project.loader
+            loader = self.instance.project.loader
             self.inner_widget.set_region_callback(
                 self.project_memory_write_func,
                 self.project_memory_read_func,
@@ -1405,7 +1405,7 @@ def debugger_memory_read_func(self, addr: int) -> HexByteValue:
         Callback to populate hex view with bytes from debugger state.
         """
         if addr not in self._data_cache:
-            dbg = self.workspace.instance.debugger_mgr.debugger
+            dbg = self.instance.debugger_mgr.debugger
             if dbg.am_none:
                 v = '?'
             else:
@@ -1433,7 +1433,7 @@ def project_memory_read_func(self, addr: int) -> HexByteValue:
         """
         Callback to populate hex view with bytes.
         """
-        p = self.workspace.instance.project
+        p = self.instance.project
 
         patches = p.kb.patches.get_all_patches(addr, 1)
         if len(patches) > 0:
@@ -1449,7 +1449,7 @@ def auto_patch(self, addr: int, new_bytes: bytearray):
         """
         Automatically update or create patches to ensure new_bytes are patched at addr.
         """
-        pm = self.workspace.instance.project.kb.patches
+        pm = self.instance.project.kb.patches
         max_addr = addr + len(new_bytes) - 1
 
         for p in pm.get_all_patches(addr, len(new_bytes)):
@@ -1491,8 +1491,8 @@ def project_memory_write_bytearray(self, addr: int, value: bytearray) -> bool:
         Callback to write array of bytes as patch.
         """
         self.auto_patch(addr, bytearray(value))
-        pm = self.workspace.instance.project.kb.patches
-        pm_notifier = self.workspace.instance.patches
+        pm = self.instance.project.kb.patches
+        pm_notifier = self.instance.patches
         if pm_notifier.am_none:
             pm_notifier.am_obj = pm
         pm_notifier.am_event()
@@ -1653,7 +1653,7 @@ def _set_breakpoint(self, bp_type: BreakpointType = BreakpointType.Execute):
         else:
             minaddr = self.inner_widget.hex.cursor
             num_bytes_selected = 1
-        self.workspace.instance.breakpoint_mgr.add_breakpoint(
+        self.instance.breakpoint_mgr.add_breakpoint(
             Breakpoint(bp_type, minaddr, num_bytes_selected)
         )
 
@@ -1801,7 +1801,7 @@ def _update_cfb_highlight_regions(self):
         Update cached list of highlight regions under cursor.
         """
         regions = []
-        cfb = self.workspace.instance.cfb
+        cfb = self.instance.cfb
         if self.smart_highlighting_enabled and not cfb.am_none:
             for item in cfb.floor_items(self.inner_widget.hex.display_start_addr):
                 item_addr, item = item
@@ -1838,22 +1838,22 @@ def _update_highlight_regions_from_patches(self):
         """
         Updates cached list of highlight regions from patches.
         """
-        if self.workspace.instance.project.am_none:
+        if self.instance.project.am_none:
             self._patch_highlights = []
         else:
             self._patch_highlights = [PatchHighlightRegion(patch, self)
-                                      for patch in self.workspace.instance.project.kb.patches.values()]
+                                      for patch in self.instance.project.kb.patches.values()]
         self._set_highlighted_regions()
 
     def _update_highlight_regions_from_breakpoints(self, **kwargs):  # pylint:disable=unused-argument
         """
         Updates cached list of highlight regions from breakpoints.
         """
-        if self.workspace.instance.project.am_none:
+        if self.instance.project.am_none:
             self._breakpoint_highlights = []
         else:
             self._breakpoint_highlights = [BreakpointHighlightRegion(bp, self)
-                                            for bp in self.workspace.instance.breakpoint_mgr.breakpoints]
+                                            for bp in self.instance.breakpoint_mgr.breakpoints]
         self._set_highlighted_regions()
 
     def _set_highlighted_regions(self):
diff --git a/angrmanagement/ui/views/interaction_view.py b/angrmanagement/ui/views/interaction_view.py
index c88b77263..050181e57 100644
--- a/angrmanagement/ui/views/interaction_view.py
+++ b/angrmanagement/ui/views/interaction_view.py
@@ -71,8 +71,8 @@ class InteractionState(enum.Enum):
 
 
 class InteractionView(BaseView):
-    def __init__(self, workspace, *args, **kwargs):
-        super().__init__('interaction', workspace, *args, **kwargs)
+    def __init__(self, instance, *args, **kwargs):
+        super().__init__('interaction', instance, *args, **kwargs)
         self.base_caption = 'Interaction'
         self.current_log = []  # for now each entry is a dict. each entry has {"dir": "in"/"out", "data": bytes} and then whatever
                        # "in" here means it's input to the program
@@ -110,7 +110,7 @@ def __init__(self, workspace, *args, **kwargs):
 
     @property
     def selected_protocol(self):
-        return self.workspace.instance.interaction_protocols[self.widget_combobox_protocol.currentIndex()]
+        return self.instance.interaction_protocols[self.widget_combobox_protocol.currentIndex()]
 
     # log_add/clear will be called by the base class. it's the subclass' responsibility to call input_show and
     # input_hide depending on whether or not the protocol is accepting input
@@ -167,13 +167,13 @@ def _handler_eof(self):
     def _handler_update_interactions(self, **kwargs):
         while self.widget_combobox_load.count():
             self.widget_combobox_load.removeItem(0)
-        for interaction in self.workspace.instance.interactions:
+        for interaction in self.instance.interactions:
             self.widget_combobox_load.addItem(interaction.name)
 
     def _handler_update_protocols(self, **kwargs):
         while self.widget_combobox_protocol.count():
             self.widget_combobox_protocol.removeItem(0)
-        for protocol in self.workspace.instance.interaction_protocols:
+        for protocol in self.instance.interaction_protocols:
             self.widget_combobox_protocol.addItem(protocol.__name__)
 
     # utility for tweaking the control panel
@@ -217,27 +217,27 @@ def _state_transition(self, state):
     # buttons
 
     def _save_interaction(self):
-        self.workspace.instance.interactions.am_obj.append(
+        self.instance.interactions.am_obj.append(
             SavedInteraction(self.widget_text_savename.text(), self.chosen_protocol, self.current_log))
-        self.workspace.instance.interactions.am_event()
+        self.instance.interactions.am_event()
 
     def _upload_interaction(self):
         if slacrs is None:
-            QMessageBox.warning(self.workspace.main_window,
+            QMessageBox.warning(self.instance.workspace.main_window,
                                 "slacrs module does not exist",
                                 "Failed to import slacrs package. Please make sure it is installed.")
             return
-        connector = self.workspace.plugins.get_plugin_instance_by_name("ChessConnector")
+        connector = self.instance.workspace.plugins.get_plugin_instance_by_name("ChessConnector")
         if connector is None:
             # chess connector does not exist
-            QMessageBox.warning(self.workspace.main_window,
+            QMessageBox.warning(self.instance.workspace.main_window,
                                 "CHESSConnector is not found",
                                 "Cannot communicate with the CHESSConnector plugin. Please make sure it is installed "
                                 "and enabled.")
             return
         if not connector.target_image_id:
             # the target image ID does not exist
-            QMessageBox.warning(self.workspace.main_window,
+            QMessageBox.warning(self.instance.workspace.main_window,
                                 "Target image ID is not specified",
                                 "The target image ID is unspecified. Please associate the binary with a remote "
                                 "challenge target.")
@@ -245,7 +245,7 @@ def _upload_interaction(self):
         slacrs_instance = connector.slacrs_instance()
         if slacrs_instance is None:
             # slacrs does not exist
-            QMessageBox.warning(self.workspace.main_window,
+            QMessageBox.warning(self.instance.workspace.main_window,
                                 "CHECRS backend does not exist",
                                 "Cannot communicate with the CHECRS backend. Please make sure you have proper Internet "
                                 "access and have connected to the CHECRS backend.")
@@ -253,7 +253,7 @@ def _upload_interaction(self):
         session = slacrs_instance.session()
 
         # get the interaction
-        interaction: SavedInteraction = self.workspace.instance.interactions[self.widget_combobox_load.currentIndex()]
+        interaction: SavedInteraction = self.instance.interactions[self.widget_combobox_load.currentIndex()]
 
         # upload it to the session
         data = b""
@@ -274,7 +274,7 @@ def _upload_interaction(self):
     def _load_interaction(self):
         if self.widget_combobox_load.currentIndex() == -1:
             return
-        thing = self.workspace.instance.interactions[self.widget_combobox_load.currentIndex()]
+        thing = self.instance.interactions[self.widget_combobox_load.currentIndex()]
         self.chosen_protocol = thing.protocol
         self.running_protocol = self.chosen_protocol(self, None)  # does this mean the abstractions are fucked?
         self._state_transition(InteractionState.VIEWING)
@@ -298,7 +298,7 @@ def _start_interaction(self):
             QtWidgets.QMessageBox.critical(None, 'Dependency error', req_msg)
             return
 
-        img_name = self.workspace.instance.img_name
+        img_name = self.instance.img_name
         if img_name is None:
             # Ask the user to provide a local image name
             QtWidgets.QMessageBox.information(None,
@@ -390,7 +390,7 @@ def _init_widgets(self):
         protocolBox = QtWidgets.QComboBox(box_start)
         box_start.layout().addWidget(protocolBox)
         self.widget_combobox_protocol = protocolBox
-        self.workspace.instance.interaction_protocols.am_subscribe(self._handler_update_protocols)
+        self.instance.interaction_protocols.am_subscribe(self._handler_update_protocols)
         self._handler_update_protocols()
 
         start_button = QtWidgets.QPushButton(box_start)
@@ -415,7 +415,7 @@ def _init_widgets(self):
         load_picker = QtWidgets.QComboBox(box_load)
         box_load.layout().addWidget(load_picker)
         self.widget_combobox_load = load_picker
-        self.workspace.instance.interactions.am_subscribe(self._handler_update_interactions)
+        self.instance.interactions.am_subscribe(self._handler_update_interactions)
         self._handler_update_interactions()
 
         load_button = QtWidgets.QPushButton(box_load)
diff --git a/angrmanagement/ui/views/log_view.py b/angrmanagement/ui/views/log_view.py
index 46acb21f9..8995cc92a 100644
--- a/angrmanagement/ui/views/log_view.py
+++ b/angrmanagement/ui/views/log_view.py
@@ -14,8 +14,8 @@ class LogView(BaseView):
     Log view displays logging output.
     """
 
-    def __init__(self, workspace, default_docking_position, *args, **kwargs):
-        super().__init__('log', workspace, default_docking_position, *args, **kwargs)
+    def __init__(self, instance, default_docking_position, *args, **kwargs):
+        super().__init__('log', instance, default_docking_position, *args, **kwargs)
 
         self.base_caption = 'Log'
         self._log_widget: QLogWidget = None
diff --git a/angrmanagement/ui/views/patches_view.py b/angrmanagement/ui/views/patches_view.py
index 07123ed64..9944678f5 100644
--- a/angrmanagement/ui/views/patches_view.py
+++ b/angrmanagement/ui/views/patches_view.py
@@ -6,8 +6,8 @@
 
 
 class PatchesView(BaseView):
-    def __init__(self, workspace, default_docking_position, *args, **kwargs):
-        super().__init__('patches', workspace, default_docking_position, *args, **kwargs)
+    def __init__(self, instance, default_docking_position, *args, **kwargs):
+        super().__init__('patches', instance, default_docking_position, *args, **kwargs)
 
         self.base_caption = "Patches"
         self._patch_table = None  # type: QPatchTable
@@ -24,7 +24,7 @@ def reload(self):
     def _init_widgets(self):
 
         # patch table
-        self._patch_table = QPatchTable(self.workspace.instance, self)
+        self._patch_table = QPatchTable(self.instance, self)
 
         #
         # controls
@@ -52,11 +52,11 @@ def _on_apply_patches_clicked(self):
         # Open File window
         file_path, _ = QFileDialog.getSaveFileName(
             self, "Save the patched binary to...",
-            self.workspace.instance.project.loader.main_object.binary + ".patched",  # FIXME: this will not work if we are loading from an angrdb
+            self.instance.project.loader.main_object.binary + ".patched",  # FIXME: this will not work if we are loading from an angrdb
             "Any file (*)",
         )
 
         if file_path:
-            b = self.workspace.instance.project.kb.patches.apply_patches_to_binary()
+            b = self.instance.project.kb.patches.apply_patches_to_binary()
             with open(file_path, "wb") as f:
                 f.write(b)
diff --git a/angrmanagement/ui/views/proximity_view.py b/angrmanagement/ui/views/proximity_view.py
index e444ac77c..7f4ed2838 100644
--- a/angrmanagement/ui/views/proximity_view.py
+++ b/angrmanagement/ui/views/proximity_view.py
@@ -22,11 +22,11 @@ class ProximityView(BaseView):
     Proximity View
     """
 
-    def __init__(self, workspace, default_docking_position, *args, **kwargs):
-        super().__init__('proximity', workspace, default_docking_position, *args, **kwargs)
+    def __init__(self, instance, default_docking_position, *args, **kwargs):
+        super().__init__('proximity', instance, default_docking_position, *args, **kwargs)
 
         self.base_caption = 'Proximity'
-        self.workspace = workspace
+        self.instance = instance
 
         self._function: Optional['Function'] = None
         self._expand_function_addrs: Set[int] = set()
@@ -55,17 +55,17 @@ def function(self, v):
 
     def get_decompilation(self):
         # FIXME: Get rid of this fucker and replace it with the new decompilation manager
-        inst = self.workspace.instance
+        inst = self.instance
         dec = inst.project.analyses.Decompiler(
             self.function,
-            cfg=self.workspace.instance.cfg,
+            cfg=self.instance.cfg,
         )
         return dec
 
     def run_analysis(self):
         dec = self.get_decompilation()
 
-        inst = self.workspace.instance
+        inst = self.instance
         prox = inst.project.analyses.Proximity(
             self.function,
             inst.cfg,
@@ -129,7 +129,7 @@ def sizeHint(self):
 
     def _init_widgets(self):
 
-        self._graph_widget = QProximityGraph(self.workspace, self)
+        self._graph_widget = QProximityGraph(self.instance, self)
 
         hlayout = QHBoxLayout()
         hlayout.addWidget(self._graph_widget)
@@ -138,7 +138,7 @@ def _init_widgets(self):
         self.setLayout(hlayout)
 
     def _register_events(self):
-        self.workspace.current_screen.am_subscribe(self.on_screen_changed)
+        self.instance.workspace.current_screen.am_subscribe(self.on_screen_changed)
 
     def _convert_node(self, node: BaseProxiNode,
                       converted: Dict[BaseProxiNode, QProximityGraphBlock]) -> Optional[QProximityGraphBlock]:
diff --git a/angrmanagement/ui/views/registers_view.py b/angrmanagement/ui/views/registers_view.py
index 2a39520b6..dabd91464 100644
--- a/angrmanagement/ui/views/registers_view.py
+++ b/angrmanagement/ui/views/registers_view.py
@@ -108,7 +108,7 @@ def __init__(self, register_view, *args, **kwargs):
 
         hheader.setSectionResizeMode(0, QHeaderView.ResizeToContents)
 
-        self._dbg_manager = register_view.workspace.instance.debugger_mgr
+        self._dbg_manager = register_view.instance.debugger_mgr
         self._dbg_watcher = DebuggerWatcher(self._on_debugger_state_updated, self._dbg_manager.debugger)
         self._on_debugger_state_updated()
 
@@ -132,8 +132,8 @@ class RegistersView(BaseView):
     Register table view.
     """
 
-    def __init__(self, workspace, default_docking_position, *args, **kwargs):
-        super().__init__('registers', workspace, default_docking_position, *args, **kwargs)
+    def __init__(self, instance, default_docking_position, *args, **kwargs):
+        super().__init__('registers', instance, default_docking_position, *args, **kwargs)
 
         self.base_caption = 'Registers'
         self._tbl_widget: Optional[QRegisterTableWidget] = None
diff --git a/angrmanagement/ui/views/stack_view.py b/angrmanagement/ui/views/stack_view.py
index 91fd9f451..34ce95875 100644
--- a/angrmanagement/ui/views/stack_view.py
+++ b/angrmanagement/ui/views/stack_view.py
@@ -97,7 +97,7 @@ def __init__(self, stack_view, *args, **kwargs):
 
         hheader.setSectionResizeMode(0, QHeaderView.ResizeToContents)
 
-        self._dbg_manager = stack_view.workspace.instance.debugger_mgr
+        self._dbg_manager = stack_view.instance.debugger_mgr
         self._dbg_watcher = DebuggerWatcher(self._on_debugger_state_updated, self._dbg_manager.debugger)
         self._on_debugger_state_updated()
 
@@ -137,7 +137,7 @@ def _set_breakpoint(self, bp_type: BreakpointType = BreakpointType.Execute):
         row = selected[0].row()
         width = state.arch.bits // 8
         offset = row * width + state.solver.eval(state.regs.sp)
-        self.stack_view.workspace.instance.breakpoint_mgr.add_breakpoint(
+        self.stack_view.instance.breakpoint_mgr.add_breakpoint(
             Breakpoint(bp_type, offset, width)
         )
 
@@ -163,8 +163,8 @@ class StackView(BaseView):
     Stack table view.
     """
 
-    def __init__(self, workspace, default_docking_position, *args, **kwargs):
-        super().__init__('stack', workspace, default_docking_position, *args, **kwargs)
+    def __init__(self, instance, default_docking_position, *args, **kwargs):
+        super().__init__('stack', instance, default_docking_position, *args, **kwargs)
 
         self.base_caption = 'Stack'
         self._tbl_widget: Optional[QStackTableWidget] = None
diff --git a/angrmanagement/ui/views/states_view.py b/angrmanagement/ui/views/states_view.py
index 5e0fef3cd..061e07d8c 100644
--- a/angrmanagement/ui/views/states_view.py
+++ b/angrmanagement/ui/views/states_view.py
@@ -6,23 +6,22 @@
 
 
 class StatesView(BaseView):
-    def __init__(self, workspace, default_docking_position, *args, **kwargs):
-        super(StatesView, self).__init__('states', workspace, default_docking_position, *args, **kwargs)
+    def __init__(self, instance, default_docking_position, *args, **kwargs):
+        super(StatesView, self).__init__('states', instance, default_docking_position, *args, **kwargs)
 
         self.base_caption = 'States'
         self._state_table = None  # type: QStateTable
-        self.workspace = workspace
 
         self._init_widgets()
 
     def reload(self):
-        self._state_table.state_manager = self.workspace.instance.states
+        self._state_table.state_manager = self.instance.states
 
     def sizeHint(self):
         return QSize(400, 800)
 
     def _init_widgets(self):
-        self._state_table = QStateTable(self.workspace.instance, self, selection_callback=self._on_state_selected)
+        self._state_table = QStateTable(self.instance, self, selection_callback=self._on_state_selected)
 
         hlayout = QHBoxLayout()
         hlayout.addWidget(self._state_table)
diff --git a/angrmanagement/ui/views/strings_view.py b/angrmanagement/ui/views/strings_view.py
index 09241d9bb..d17e0eae0 100644
--- a/angrmanagement/ui/views/strings_view.py
+++ b/angrmanagement/ui/views/strings_view.py
@@ -11,8 +11,8 @@
 
 
 class StringsView(BaseView):
-    def __init__(self, workspace, default_docking_position, *args, **kwargs):
-        super(StringsView, self).__init__('strings', workspace, default_docking_position, *args, **kwargs)
+    def __init__(self, instance, default_docking_position, *args, **kwargs):
+        super(StringsView, self).__init__('strings', instance, default_docking_position, *args, **kwargs)
 
         self.base_caption = 'Strings'
 
@@ -25,11 +25,11 @@ def __init__(self, workspace, default_docking_position, *args, **kwargs):
         self.reload()
 
     def reload(self):
-        if self.workspace.instance.kb is None:
+        if self.instance.kb is None:
             return
-        self._function_list.functions = self.workspace.instance.kb.functions
-        self._string_table.cfg = self.workspace.instance.cfg
-        self._string_table.xrefs = self.workspace.instance.project.kb.xrefs
+        self._function_list.functions = self.instance.kb.functions
+        self._string_table.cfg = self.instance.cfg
+        self._string_table.xrefs = self.instance.project.kb.xrefs
         self._string_table.function = self._selected_function
 
     def sizeHint(self):
@@ -58,14 +58,14 @@ def _on_string_selected(self, s: MemoryData):
         :return:
         """
 
-        if len(self.workspace.view_manager.views_by_category['disassembly']) == 1:
-            disasm_view = self.workspace.view_manager.first_view_in_category('disassembly')
+        if len(self.instance.workspace.view_manager.views_by_category['disassembly']) == 1:
+            disasm_view = self.instance.workspace.view_manager.first_view_in_category('disassembly')
         else:
-            disasm_view = self.workspace.view_manager.current_view_in_category('disassembly')
+            disasm_view = self.instance.workspace.view_manager.current_view_in_category('disassembly')
         if disasm_view is not None:
             disasm_view.jump_to(s.addr)
             disasm_view.select_label(s.addr)
-            self.workspace.view_manager.raise_view(disasm_view)
+            self.instance.workspace.view_manager.raise_view(disasm_view)
 
     def on_filter_change(self, **kwargs): #pylint: disable=unused-argument
         pattern = self._filter_string.text()
diff --git a/angrmanagement/ui/views/symexec_view.py b/angrmanagement/ui/views/symexec_view.py
index 8ca56c04a..928dc6752 100644
--- a/angrmanagement/ui/views/symexec_view.py
+++ b/angrmanagement/ui/views/symexec_view.py
@@ -9,8 +9,8 @@
 
 
 class SymexecView(BaseView):
-    def __init__(self, workspace, *args, **kwargs):
-        super().__init__('symexec', workspace, *args, **kwargs)
+    def __init__(self, instance, *args, **kwargs):
+        super().__init__('symexec', instance, *args, **kwargs)
 
         self.base_caption = 'Symbolic Execution'
 
@@ -54,7 +54,7 @@ def view_state(self, state):
         self._state_viewer.state = state
 
         # push namespace into the console
-        view = self.workspace.view_manager.first_view_in_category('console')
+        view = self.instance.workspace.view_manager.first_view_in_category('console')
         if view is not None:
             view.push_namespace({
                 'state': state,
@@ -93,18 +93,18 @@ def _init_widgets(self):
         # main.setCorner(Qt.TopLeftCorner, Qt.TopDockWidgetArea)
         # main.setCorner(Qt.TopRightCorner, Qt.RightDockWidgetArea)
 
-        pathtree = QPathTree(self.current_simgr, self.current_state, self, self.workspace, parent=main)
+        pathtree = QPathTree(self.current_simgr, self.current_state, self, self.instance.workspace, parent=main)
         pathtree_dock = QDockWidget('PathTree', pathtree)
         main.setCentralWidget(pathtree_dock)
         # main.addDockWidget(Qt.BottomDockWidgetArea, pathtree_dock)
         pathtree_dock.setWidget(pathtree)
 
-        simgrs = QSimulationManagers(self.workspace.instance, self.current_simgr, self.current_state, parent=main)
+        simgrs = QSimulationManagers(self.instance, self.current_simgr, self.current_state, parent=main)
         simgrs_dock = QDockWidget('SimulationManagers', simgrs)
         main.addDockWidget(Qt.RightDockWidgetArea, simgrs_dock)
         simgrs_dock.setWidget(simgrs)
 
-        state_viewer = StateInspector(self.workspace, self.current_state, parent=self)
+        state_viewer = StateInspector(self.instance.workspace, self.current_state, parent=self)
         state_viewer_dock = QDockWidget('Selected State', state_viewer)
         main.addDockWidget(Qt.RightDockWidgetArea, state_viewer_dock)
         state_viewer_dock.setWidget(state_viewer)
@@ -125,9 +125,9 @@ def _init_widgets(self):
 
     def _switch_to_disassembly_view(self, addr):
         if len(self.view_manager.views_by_category['disassembly']) == 1:
-            disasm_view = self.workspace.view_manager.first_view_in_category('disassembly')
+            disasm_view = self.instance.workspace.view_manager.first_view_in_category('disassembly')
         else:
-            disasm_view = self.workspace.view_manager.current_view_in_category('disassembly')
+            disasm_view = self.instance.workspace.view_manager.current_view_in_category('disassembly')
         disasm_view.jump_to(addr)
 
-        self.workspace.raise_view(disasm_view)
+        self.instance.workspace.raise_view(disasm_view)
diff --git a/angrmanagement/ui/views/trace_map_view.py b/angrmanagement/ui/views/trace_map_view.py
index fc4ea2bfe..83d29fd68 100644
--- a/angrmanagement/ui/views/trace_map_view.py
+++ b/angrmanagement/ui/views/trace_map_view.py
@@ -12,8 +12,8 @@ class TraceMapView(BaseView):
     View container for QTraceMap.
     """
 
-    def __init__(self, workspace: 'Workspace', default_docking_position, *args, **kwargs):
-        super().__init__('tracemap', workspace, default_docking_position, *args, **kwargs)
+    def __init__(self, instance, default_docking_position, *args, **kwargs):
+        super().__init__('tracemap', instance, default_docking_position, *args, **kwargs)
         self.base_caption: str = 'Trace Map'
         self.inner_widget: Optional[QTraceMap] = None
         self._init_widgets()
diff --git a/angrmanagement/ui/views/traces_view.py b/angrmanagement/ui/views/traces_view.py
index 88a776488..b2825bcca 100644
--- a/angrmanagement/ui/views/traces_view.py
+++ b/angrmanagement/ui/views/traces_view.py
@@ -119,8 +119,8 @@ class TracesView(BaseView):
     Traces table view.
     """
 
-    def __init__(self, workspace, default_docking_position, *args, **kwargs):
-        super().__init__('traces', workspace, default_docking_position, *args, **kwargs)
+    def __init__(self, instance, default_docking_position, *args, **kwargs):
+        super().__init__('traces', instance, default_docking_position, *args, **kwargs)
 
         self.base_caption = 'Traces'
         self._tbl_widget: Optional[QTraceTableWidget] = None
@@ -136,6 +136,6 @@ def minimumSizeHint(*args, **kwargs):  # pylint:disable=unused-argument
 
     def _init_widgets(self):
         vlayout = QVBoxLayout()
-        self._tbl_widget = QTraceTableWidget(self.workspace.instance, self)
+        self._tbl_widget = QTraceTableWidget(self.instance, self)
         vlayout.addWidget(self._tbl_widget)
         self.setLayout(vlayout)
diff --git a/angrmanagement/ui/views/types_view.py b/angrmanagement/ui/views/types_view.py
index 136bd0a76..4d58d54ab 100644
--- a/angrmanagement/ui/views/types_view.py
+++ b/angrmanagement/ui/views/types_view.py
@@ -21,8 +21,8 @@ class TypesView(BaseView):
 
     FUNCTION_SPECIFIC_VIEW = True
 
-    def __init__(self, workspace, default_docking_position, *args, **kwargs):
-        super().__init__('types', workspace, default_docking_position, *args, **kwargs)
+    def __init__(self, instance, default_docking_position, *args, **kwargs):
+        super().__init__('types', instance, default_docking_position, *args, **kwargs)
 
         self.base_caption = 'Types'
 
@@ -52,8 +52,8 @@ def function(self, v):
     @property
     def current_typestore(self) -> 'TypesStore':
         if self._function.am_none:
-            return self.workspace.instance.kb.types
-        var_manager: 'VariableManagerInternal' = self.workspace.instance.pseudocode_variable_kb.variables[
+            return self.instance.kb.types
+        var_manager: 'VariableManagerInternal' = self.instance.pseudocode_variable_kb.variables[
             self._function.addr]
         return var_manager.types
 
@@ -118,10 +118,10 @@ def reload(self):
     def _on_new_type(self):
         dialog = CTypeEditor(
             None,
-            self.workspace.instance.project.arch,
+            self.instance.project.arch,
             multiline=True,
             allow_multiple=True,
-            predefined_types=self.workspace.instance.kb.types
+            predefined_types=self.instance.kb.types
         )
         dialog.exec_()
 
diff --git a/angrmanagement/ui/views/view.py b/angrmanagement/ui/views/view.py
index f858d9908..862fead2a 100644
--- a/angrmanagement/ui/views/view.py
+++ b/angrmanagement/ui/views/view.py
@@ -8,6 +8,7 @@
 
 if TYPE_CHECKING:
     from angrmanagement.ui.workspace import Workspace
+    from angrmanagement.ui.workspace import Instance
 
 
 class BaseView(QFrame):
@@ -19,10 +20,10 @@ class BaseView(QFrame):
     # to True.
     FUNCTION_SPECIFIC_VIEW = False
 
-    def __init__(self, category: str, workspace, default_docking_position, *args, **kwargs):
+    def __init__(self, category: str, instance, default_docking_position, *args, **kwargs):
         super().__init__(*args, **kwargs)
 
-        self.workspace: 'Workspace' = workspace
+        self.instance: 'Instance' = instance
         self.category = category
         self.default_docking_position = default_docking_position
 
@@ -42,7 +43,7 @@ def function(self, v):
         raise NotImplementedError()
 
     def focus(self):
-        self.workspace.view_manager.raise_view(self)
+        self.instance.workspace.view_manager.raise_view(self)
 
     def refresh(self):
         pass
@@ -62,7 +63,7 @@ def is_shown(self):
         return self.visibleRegion().isEmpty() is False
 
     def closeEvent(self, event: PySide2.QtGui.QCloseEvent):
-        self.workspace.view_manager.remove_view(self)
+        self.instance.workspace.view_manager.remove_view(self)
         event.accept()
 
     #
@@ -200,7 +201,7 @@ def get_synchronize_with_submenu(self) -> QMenu:
         Get submenu for 'Synchronize with' context menu.
         """
         mnu = QMenu("&Synchronize with", self)
-        groups = {v.sync_state for v in self.workspace.view_manager.views
+        groups = {v.sync_state for v in self.instance.workspace.view_manager.views
                   if (v is not self) and isinstance(v, SynchronizedView)}
         if len(groups) == 0:
             act = QAction('None available', self)
diff --git a/angrmanagement/ui/widgets/qaddress_input.py b/angrmanagement/ui/widgets/qaddress_input.py
index befe763d4..c91f24e82 100644
--- a/angrmanagement/ui/widgets/qaddress_input.py
+++ b/angrmanagement/ui/widgets/qaddress_input.py
@@ -2,16 +2,13 @@
 
 from PySide2.QtWidgets import QLineEdit
 
-if TYPE_CHECKING:
-    from angrmanagement.ui.workspace import Workspace
-
 
 class QAddressInput(QLineEdit):
-    def __init__(self, textchanged_callback: Optional[Callable], workspace: 'Workspace', parent=None,
+    def __init__(self, textchanged_callback: Optional[Callable], instance, parent=None,
                  default: Optional[str]=None):
         super(QAddressInput, self).__init__(parent)
 
-        self.workspace = workspace
+        self.instance = instance
 
         if default is not None:
             self.setText(str(default))
@@ -47,7 +44,7 @@ def _convert_to_addr(self, input_):
             pass
 
         # is it a function name?
-        functions = self.workspace.instance.project.kb.functions
+        functions = self.instance.project.kb.functions
         func = functions.function(name=input_, create=False)
         if func is not None:
             return func.addr
diff --git a/angrmanagement/ui/widgets/qblock.py b/angrmanagement/ui/widgets/qblock.py
index 9106535bb..44754b936 100644
--- a/angrmanagement/ui/widgets/qblock.py
+++ b/angrmanagement/ui/widgets/qblock.py
@@ -32,12 +32,12 @@ class QBlock(QCachedGraphicsItem):
     SHADOW_OFFSET_X = 0
     SHADOW_OFFSET_Y = 0
 
-    def __init__(self, workspace, func_addr, disasm_view, disasm, infodock, addr, cfg_nodes, out_branches, scene,
+    def __init__(self, instance, func_addr, disasm_view, disasm, infodock, addr, cfg_nodes, out_branches, scene,
                  parent=None):
         super().__init__(parent=parent)
 
         # initialization
-        self.workspace = workspace
+        self.instance = instance
         self.func_addr = func_addr
         self.disasm_view = disasm_view
         self.disasm = disasm
@@ -145,22 +145,22 @@ def _init_ail_block_widgets(self):
         bn = self.cfg_nodes
         if bn.addr in self.disasm.kb.labels:
             label = QBlockLabel(bn.addr, get_label_text(bn.addr, self.disasm.kb),
-                                self._config, self.disasm_view, self.workspace,
+                                self._config, self.disasm_view, self.instance,
                                 self.infodock, parent=self)
             self.objects.append(label)
             self.addr_to_labels[bn.addr] = label
 
         # always add the block name as a label and instruction:
         block_name_label = QBlockLabel(bn.addr, f"loc_{hex(bn.addr)}:", self._config,
-                                       self.disasm_view, self.workspace,
+                                       self.disasm_view, self.instance,
                                        self.infodock, parent=self)
         self.objects.append(block_name_label)
         self.addr_to_labels[bn.addr] = block_name_label
 
         for stmt in bn.statements:
-            code_obj = QAilObj(stmt, self.workspace, self.infodock, parent=None, options=self._block_code_options)
+            code_obj = QAilObj(stmt, self.instance, self.infodock, parent=None, options=self._block_code_options)
             obj = QBlockCode(stmt.ins_addr, code_obj, self._config, self.disasm_view,
-                             self.workspace, self.infodock, parent=self)
+                             self.instance, self.infodock, parent=self)
             code_obj.parent = obj # Reparent
             self.objects.append(obj)
             self.addr_to_insns[bn.addr] = obj
@@ -169,32 +169,32 @@ def _init_disassembly_block_widgets(self):
         for obj in get_block_objects(self.disasm, self.cfg_nodes, self.func_addr):
             if isinstance(obj, Instruction):
                 out_branch = get_out_branches_for_insn(self.out_branches, obj.addr)
-                insn = QInstruction(self.workspace, self.func_addr, self.disasm_view, self.disasm,
+                insn = QInstruction(self.instance, self.func_addr, self.disasm_view, self.disasm,
                                     self.infodock, obj, out_branch, self._config, parent=self)
                 self.objects.append(insn)
                 self.addr_to_insns[obj.addr] = insn
             elif isinstance(obj, Label):
-                label = QBlockLabel(obj.addr, obj.text, self._config, self.disasm_view, self.workspace, self.infodock,
+                label = QBlockLabel(obj.addr, obj.text, self._config, self.disasm_view, self.instance, self.infodock,
                                     parent=self)
                 self.objects.append(label)
                 self.addr_to_labels[obj.addr] = label
             elif isinstance(obj, IROp):
                 code_obj = QIROpObj(obj, self.infodock, parent=None)
                 disp_obj = QBlockCode(obj.addr, code_obj, self._config, self.disasm_view,
-                                 self.workspace, self.infodock, parent=self)
+                                 self.instance, self.infodock, parent=self)
                 code_obj.parent = disp_obj # Reparent
                 self.objects.append(disp_obj)
             elif isinstance(obj, PhiVariable):
                 if not isinstance(obj.variable, SimRegisterVariable):
-                    phivariable = QPhiVariable(self.workspace, self.disasm_view, obj, self._config, parent=self)
+                    phivariable = QPhiVariable(self.instance, self.disasm_view, obj, self._config, parent=self)
                     self.objects.append(phivariable)
             elif isinstance(obj, Variables):
                 for var in obj.variables:
-                    variable = QVariable(self.workspace, self.disasm_view, var, self._config, parent=self)
+                    variable = QVariable(self.instance, self.disasm_view, var, self._config, parent=self)
                     self.objects.append(variable)
             elif isinstance(obj, FunctionHeader):
                 self.objects.append(QFunctionHeader(self.func_addr, obj.name, obj.prototype, obj.args, self._config,
-                                                    self.disasm_view, self.workspace, self.infodock, parent=self))
+                                                    self.disasm_view, self.instance, self.infodock, parent=self))
 
     def _init_widgets(self):
         if self.scene is not None:
@@ -253,7 +253,7 @@ def hoverLeaveEvent(self, event):
         event.accept()
 
     def mousePressEvent(self, event):
-        if self.workspace.plugins.handle_click_block(self, event):
+        if self.instance.workspace.plugins.handle_click_block(self, event):
             # stop handling this event if the event has been handled by a plugin
             event.accept()
             return
@@ -264,7 +264,7 @@ def mousePressEvent(self, event):
         super().mousePressEvent(event)
 
     def _calc_backcolor(self, should_omit_text):
-        color = self.workspace.plugins.color_block(self.addr)
+        color = self.instance.workspace.plugins.color_block(self.addr)
         if color is not None:
             return color
 
@@ -314,7 +314,7 @@ def paint(self, painter, option, widget):  # pylint: disable=unused-argument
                 self._objects_are_hidden = should_omit_text
 
         # extra content
-        self.workspace.plugins.draw_block(self, painter)
+        self.instance.workspace.plugins.draw_block(self, painter)
 
     def on_selected(self):
         self.infodock.select_block(self.addr)
diff --git a/angrmanagement/ui/widgets/qblock_code.py b/angrmanagement/ui/widgets/qblock_code.py
index a0e1a41d0..7fe78ecfd 100644
--- a/angrmanagement/ui/widgets/qblock_code.py
+++ b/angrmanagement/ui/widgets/qblock_code.py
@@ -180,9 +180,9 @@ class QAilObj(QBlockCodeObj):
     Renders an AIL object
     """
 
-    def __init__(self, obj:Any, workspace, *args, stmt=None, **kwargs):
+    def __init__(self, obj:Any, instance, *args, stmt=None, **kwargs):
         self.stmt = stmt or obj
-        self.workspace = workspace
+        self.instance = instance
         super().__init__(obj, *args, **kwargs)
 
     def create_subobjs(self, obj:Any):
@@ -207,7 +207,7 @@ def add_ailobj(self, obj:Any):
             ailment.expression.Convert: QAilConvertObj,
             ailment.expression.Load: QAilLoadObj,
         }.get(type(obj), QAilTextObj)
-        subobj = subobjcls(obj, self.workspace, self.infodock, parent=self, options=self.options, stmt=self.stmt)
+        subobj = subobjcls(obj, self.instance, self.infodock, parent=self, options=self.options, stmt=self.stmt)
         self._add_subobj(subobj)
 
     @property
@@ -337,9 +337,9 @@ def create_subobjs(self, obj:ailment.expression.Const):
             return
 
         data_str = string_at_addr(
-            self.workspace.instance.cfg,
+            self.instance.cfg,
             obj.value,
-            self.workspace.instance.project,
+            self.instance.project,
          )
         if data_str:
             self.add_text(data_str)
@@ -716,12 +716,11 @@ class QBlockCode(QCachedGraphicsItem):
     obj: QBlockCodeObj
     _config: ConfigurationManager
     disasm_view: 'QDisassemblyBaseControl'
-    workspace: 'Workspace'
     infodock: InfoDock
     parent: Any
 
     def __init__(self, addr:int, obj:QBlockCodeObj, config:ConfigurationManager,
-        disasm_view:'QDisassemblyBaseControl', workspace:'Workspace',
+        disasm_view:'QDisassemblyBaseControl', instance,
         infodock:InfoDock, parent:Any=None):
         super().__init__(parent=parent)
         self.addr = addr
@@ -732,7 +731,7 @@ def __init__(self, addr:int, obj:QBlockCodeObj, config:ConfigurationManager,
         self._height = 0
         self._config = config
         self.parent = parent
-        self.workspace = workspace
+        self.instance = instance
         self.infodock = infodock
         self._disasm_view = disasm_view
         self._qtextdoc = QTextDocument()
diff --git a/angrmanagement/ui/widgets/qblock_label.py b/angrmanagement/ui/widgets/qblock_label.py
index ffc630938..4b2c573d1 100644
--- a/angrmanagement/ui/widgets/qblock_label.py
+++ b/angrmanagement/ui/widgets/qblock_label.py
@@ -9,10 +9,10 @@
 
 class QBlockLabel(QCachedGraphicsItem):
 
-    def __init__(self, addr, text, config, disasm_view, workspace, infodock, parent=None):
+    def __init__(self, addr, text, config, disasm_view, instance, infodock, parent=None):
         super().__init__(parent=parent)
 
-        self.workspace = workspace
+        self.instance = instance
         self.addr = addr
         self.text = text
         self._width = 0
diff --git a/angrmanagement/ui/widgets/qccode_edit.py b/angrmanagement/ui/widgets/qccode_edit.py
index d8185a9a5..b5aa003ee 100644
--- a/angrmanagement/ui/widgets/qccode_edit.py
+++ b/angrmanagement/ui/widgets/qccode_edit.py
@@ -128,7 +128,7 @@ def get_context_menu(self):
             self._selected_node = under_cursor
             mnu.addActions(self.function_name_actions)
             for entry in self.workspace.plugins.build_context_menu_functions(
-                    [self.workspace.instance.kb.functions[under_cursor.name]]):
+                    [self.instance.kb.functions[under_cursor.name]]):
                 Menu.translate_element(mnu, entry)
         else:
             mnu.addActions(self.default_actions)
@@ -140,7 +140,11 @@ def get_context_menu(self):
 
     @property
     def workspace(self):
-        return self._code_view.workspace if self._code_view is not None else None
+        return self._code_view.instance.workspace if self._code_view is not None else None
+
+    @property
+    def instance(self):
+        return self._code_view.instance if self._code_view is not None else None
 
     def event(self, event):
         """
@@ -274,7 +278,7 @@ def retype_node(self, *args, node=None, node_type=None):  # pylint: disable=unus
         if isinstance(node, CVariable) and isinstance(node.variable, SimTemporaryVariable):
             # unsupported right now..
             return
-        dialog = RetypeNode(self.workspace.instance, code_view=self._code_view, node=node,
+        dialog = RetypeNode(self.instance, code_view=self._code_view, node=node,
                             node_type=node_type)
         dialog.exec_()
 
@@ -282,10 +286,9 @@ def retype_node(self, *args, node=None, node_type=None):  # pylint: disable=unus
         if new_node_type is not None:
             if self._code_view is not None and node is not None:
                 # need workspace for altering callbacks of changes
-                workspace = self._code_view.workspace
                 variable_kb = self._code_view.codegen._variable_kb
                 # specify the type
-                new_node_type = new_node_type.with_arch(workspace.instance.project.arch)
+                new_node_type = new_node_type.with_arch(self.instance.project.arch)
                 variable_kb.variables[self._code_view.function.addr].set_variable_type(
                     node.variable,
                     new_node_type,
@@ -373,7 +376,7 @@ def convert_to_ite_expr(self):
         if addr is None:
             return
 
-        cache = self.workspace.instance.kb.structured_code[(self._code_view.function.addr, "pseudocode")]
+        cache = self.instance.kb.structured_code[(self._code_view.function.addr, "pseudocode")]
         if cache.ite_exprs is None:
             cache.ite_exprs = set()
         cache.ite_exprs.add((addr, ailexpr))
@@ -402,7 +405,7 @@ def swap_binop_operands(self):
         if addr is None:
             return
 
-        cache = self.workspace.instance.kb.structured_code[(self._code_view.function.addr, "pseudocode")]
+        cache = self.instance.kb.structured_code[(self._code_view.function.addr, "pseudocode")]
         if cache.binop_operators is None:
             cache.binop_operators = { }
         op_desc = OpDescriptor(ailexpr.vex_block_addr if hasattr(ailexpr, "vex_block_addr") else None,
@@ -460,7 +463,7 @@ def _find_loads(expr) -> list:
             return
 
         # traverse the stored clinic graph to find the AIL block
-        cache = self.workspace.instance.kb.structured_code[(self._code_view.function.addr, "pseudocode")]
+        cache = self.instance.kb.structured_code[(self._code_view.function.addr, "pseudocode")]
         the_node = None
         for node in cache.clinic.graph.nodes():
             if node.addr <= ins_addr < node.addr + node.original_size:
diff --git a/angrmanagement/ui/widgets/qdecomp_options.py b/angrmanagement/ui/widgets/qdecomp_options.py
index 52f41f62e..4e23c6e34 100644
--- a/angrmanagement/ui/widgets/qdecomp_options.py
+++ b/angrmanagement/ui/widgets/qdecomp_options.py
@@ -137,13 +137,13 @@ def get_default_passes(self):
         if self._instance is None or self._instance.project.am_none:
             return []
         return get_default_optimization_passes(self._instance.project.arch, self._instance.project.simos.name) + \
-            [x for x, de, in self._code_view.workspace.plugins.optimization_passes() if de]
+            [x for x, de, in self._code_view.instance.workspace.plugins.optimization_passes() if de]
 
     def get_all_passes(self):
         if self._instance is None or self._instance.project.am_none:
             return []
         return get_optimization_passes(self._instance.project.arch, self._instance.project.simos.name) + \
-            [x for x, _, in self._code_view.workspace.plugins.optimization_passes()]
+            [x for x, _, in self._code_view.instance.workspace.plugins.optimization_passes()]
 
     def get_default_peephole_opts(self):  # pylint: disable=no-self-use
         return STMT_OPTS + EXPR_OPTS
diff --git a/angrmanagement/ui/widgets/qdepgraph_block.py b/angrmanagement/ui/widgets/qdepgraph_block.py
index 8f2d4218b..528914290 100644
--- a/angrmanagement/ui/widgets/qdepgraph_block.py
+++ b/angrmanagement/ui/widgets/qdepgraph_block.py
@@ -31,7 +31,7 @@ def __init__(self, is_selected, dep_view: 'DependencyView', definition: 'Definit
         super().__init__()
 
         self._dep_view = dep_view
-        self._workspace = self._dep_view.workspace
+        self._instance = self._dep_view.instance
         self._config = Conf
 
         self.selected = is_selected
@@ -66,7 +66,7 @@ def _init_widgets(self):
         addr_str = "unknown address" if self.addr is None else "%#x" % self.addr
         if isinstance(atom, Register):
             # convert it to a register name
-            arch = self._workspace.instance.project.arch
+            arch = self._instance.project.arch
             register_name = arch.translate_register_name(atom.reg_offset, size=atom.size)
             self._definition_str = "Register {} @ {}".format(register_name, addr_str)
         elif isinstance(atom, MemoryLocation):
@@ -86,11 +86,11 @@ def _init_widgets(self):
             self._instruction_str = "Unknown"
         else:
             # function string
-            the_func = locate_function(self._workspace.instance, self.addr)
+            the_func = locate_function(self._instance, self.addr)
             if the_func is None:
                 # is it a SimProcedure?
-                if self._workspace.instance.project.is_hooked(self.addr):
-                    hooker = self._workspace.instance.project.hooked_by(self.addr)
+                if self._instance.project.is_hooked(self.addr):
+                    hooker = self._instance.project.hooked_by(self.addr)
                     self._function_str = "SimProcedure " + hooker.__class__.__name__.split('.')[-1]
                 else:
                     self._function_str = "Unknown"
@@ -102,11 +102,11 @@ def _init_widgets(self):
                     self._function_str = "%s%+x" % (the_func.name, offset)
             # instruction
             self._instruction_str = "%s:  %s" % (self._function_str,
-                                                 self._workspace.instance.get_instruction_text_at(self.addr))
+                                                 self._instance.get_instruction_text_at(self.addr))
             # text
-            self._text = get_string_for_display(self._workspace.instance.cfg,
+            self._text = get_string_for_display(self._instance.cfg,
                                                 self.addr,
-                                                self._workspace.instance.project,
+                                                self._instance.project,
                                                 max_size=60,
                                                 )
 
diff --git a/angrmanagement/ui/widgets/qdisasm_base_control.py b/angrmanagement/ui/widgets/qdisasm_base_control.py
index 5828cb5a7..c0de92dd5 100644
--- a/angrmanagement/ui/widgets/qdisasm_base_control.py
+++ b/angrmanagement/ui/widgets/qdisasm_base_control.py
@@ -19,8 +19,8 @@ class QDisassemblyBaseControl:
     The base control class of QLinearViewer and QDisassemblyGraph. Implements or declares common shorthands and methods.
     """
 
-    def __init__(self, workspace, disasm_view, base_cls):
-        self.workspace = workspace
+    def __init__(self, instance, disasm_view, base_cls):
+        self.instance = instance
         self.disasm_view: 'DisassemblyView' = disasm_view
         self._base_cls = base_cls
         self._insaddr_to_block = { }
diff --git a/angrmanagement/ui/widgets/qdisasm_graph.py b/angrmanagement/ui/widgets/qdisasm_graph.py
index bb43e9469..4d87d4512 100644
--- a/angrmanagement/ui/widgets/qdisasm_graph.py
+++ b/angrmanagement/ui/widgets/qdisasm_graph.py
@@ -48,11 +48,11 @@ def _set_pos(self, step):
 
 class QDisassemblyGraph(QDisassemblyBaseControl, QZoomableDraggableGraphicsView):
 
-    def __init__(self, workspace, disasm_view, parent=None):
-        QDisassemblyBaseControl.__init__(self, workspace, disasm_view, QZoomableDraggableGraphicsView)
+    def __init__(self, instance, disasm_view, parent=None):
+        QDisassemblyBaseControl.__init__(self, instance, disasm_view, QZoomableDraggableGraphicsView)
         QZoomableDraggableGraphicsView.__init__(self, parent=parent)
 
-        self.workspace = workspace
+        self.instance = instance
 
         self.disasm = None
         self.variable_manager = None
@@ -124,18 +124,18 @@ def reload(self, old_infodock: Optional['InfoDock']=None):
             func = self._function_graph.function
             try:
                 # always check if decompiler has cached a clinic object first
-                self.disasm = self.workspace.instance.kb.structured_code[(func.addr, 'pseudocode')].clinic
+                self.disasm = self.instance.kb.structured_code[(func.addr, 'pseudocode')].clinic
             except (KeyError, AttributeError):
-                self.disasm = self.workspace.instance.project.analyses.Clinic(func)
+                self.disasm = self.instance.project.analyses.Clinic(func)
 
             self._supergraph = to_ail_supergraph(self.disasm.cc_graph)
             nodefunc = lambda n: n
             branchfunc = lambda n: None
         else:
             include_ir = self._disassembly_level is DisassemblyLevel.LifterIR
-            self.disasm = self.workspace.instance.project.analyses.Disassembly(function=self._function_graph.function,
+            self.disasm = self.instance.project.analyses.Disassembly(function=self._function_graph.function,
                                                                                include_ir=include_ir)
-            view = self.workspace.view_manager.first_view_in_category('console')
+            view = self.instance.workspace.view_manager.first_view_in_category('console')
             if view is not None:
                 view.push_namespace({
                     'disasm': self.disasm,
@@ -145,7 +145,7 @@ def reload(self, old_infodock: Optional['InfoDock']=None):
             branchfunc = get_out_branches
 
         for n in self._supergraph.nodes():
-            block = QGraphBlock(self.workspace, self._function_graph.function.addr, self.disasm_view, self.disasm,
+            block = QGraphBlock(self.instance, self._function_graph.function.addr, self.disasm_view, self.disasm,
                                 self.infodock, n.addr, nodefunc(n), branchfunc(n), scene)
             if n.addr == self._function_graph.function.addr:
                 self.entry_block = block
diff --git a/angrmanagement/ui/widgets/qfeature_map.py b/angrmanagement/ui/widgets/qfeature_map.py
index be1647365..0959bda54 100644
--- a/angrmanagement/ui/widgets/qfeature_map.py
+++ b/angrmanagement/ui/widgets/qfeature_map.py
@@ -35,8 +35,8 @@ def __init__(self, disasm_view, *args, **kwargs):
         self._height: int = 1
 
         self.disasm_view = disasm_view
-        self.workspace = disasm_view.workspace
-        self.instance = self.workspace.instance
+        self.workspace = disasm_view.instance.workspace
+        self.instance = self.disasm_view.instance
 
         self.addr = ObjectContainer(None, name='The current address of the Feature Map.')
 
@@ -345,7 +345,7 @@ def on_mouse_move_event_from_view(self, point: QPointF):
 
         try:
             addr = self._get_addr_from_pos(point.x())
-            item = self.workspace.instance.cfb.floor_item(addr)
+            item = self.instance.cfb.floor_item(addr)
             if item is not None:
                 _, item = item
                 self.setToolTip(f'{str(item)} in {str(mr)}')
diff --git a/angrmanagement/ui/widgets/qfunction_table.py b/angrmanagement/ui/widgets/qfunction_table.py
index fbe5da6c3..b3ee4adbd 100644
--- a/angrmanagement/ui/widgets/qfunction_table.py
+++ b/angrmanagement/ui/widgets/qfunction_table.py
@@ -32,12 +32,12 @@ class QFunctionTableModel(QAbstractTableModel):
     SIZE_COL = 4
     BLOCKS_COL = 5
 
-    def __init__(self, workspace, func_list):
+    def __init__(self, instance, func_list):
         super().__init__()
 
         self._func_list = None
         self._raw_func_list = func_list
-        self.workspace = workspace
+        self.instance = instance
         self._config = Conf
 
     def __len__(self):
@@ -64,7 +64,7 @@ def filter(self, keyword):
             # remove the filtering
             self._func_list = None
         else:
-            extra_columns = self.workspace.plugins.count_func_columns()
+            extra_columns = self.instance.workspace.plugins.count_func_columns()
             self._func_list = [ func for func in self._raw_func_list if
                                 self._func_match_keyword(func, keyword, extra_columns=extra_columns) ]
 
@@ -76,7 +76,7 @@ def rowCount(self, *args, **kwargs): # pylint:disable=unused-argument
         return len(self.func_list)
 
     def columnCount(self, *args, **kwargs): # pylint:disable=unused-argument
-        return len(self.Headers) + self.workspace.plugins.count_func_columns()
+        return len(self.Headers) + self.instance.workspace.plugins.count_func_columns()
 
     def headerData(self, section, orientation, role): # pylint:disable=unused-argument
         if role != Qt.DisplayRole:
@@ -86,7 +86,7 @@ def headerData(self, section, orientation, role): # pylint:disable=unused-argume
             return self.Headers[section]
         else:
             try:
-                return self.workspace.plugins.get_func_column(section - len(self.Headers))
+                return self.instance.workspace.plugins.get_func_column(section - len(self.Headers))
             except IndexError:
                 # Not enough columns
                 return None
@@ -124,7 +124,7 @@ def data(self, index, role):
             return QBrush(color)
 
         elif role == Qt.BackgroundColorRole:
-            color = self.workspace.plugins.color_func(func)
+            color = self.instance.workspace.plugins.color_func(func)
             if color is None:
                 # default colors
                 if func.from_signature:
@@ -160,7 +160,7 @@ def _get_column_data(self, func, idx):
         elif idx == self.BLOCKS_COL:
             return len(func.block_addrs_set)
         else:
-            return self.workspace.plugins.extract_func_column(func, idx - len(self.Headers))[0]
+            return self.instance.workspace.plugins.extract_func_column(func, idx - len(self.Headers))[0]
 
     def _get_column_text(self, func, idx):
         if idx < len(self.Headers):
@@ -172,7 +172,7 @@ def _get_column_text(self, func, idx):
             else:
                 return str(data)
 
-        return self.workspace.plugins.extract_func_column(func, idx - len(self.Headers))[1]
+        return self.instance.workspace.plugins.extract_func_column(func, idx - len(self.Headers))[1]
 
     @staticmethod
     def _get_binary_name(func):
@@ -219,7 +219,7 @@ def _func_match_keyword(self, func, keyword, extra_columns: int=0):
             return True
         if extra_columns > 0:
             for idx in range(extra_columns):
-                txt = self.workspace.plugins.extract_func_column(func, idx)[1]
+                txt = self.instance.workspace.plugins.extract_func_column(func, idx)[1]
                 if txt and keyword in txt.lower():
                     return True
         return False
@@ -229,9 +229,9 @@ class QFunctionTableView(QTableView):
     """
     The table view for QFunctionTable.
     """
-    def __init__(self, parent, workspace, selection_callback=None):
+    def __init__(self, parent, instance, selection_callback=None):
         super().__init__(parent)
-        self.workspace = workspace
+        self.instance = instance
         self._context_menu = FunctionContextMenu(self)
 
         self._function_table = parent  # type: QFunctionTable
@@ -252,7 +252,7 @@ def __init__(self, parent, workspace, selection_callback=None):
 
         self.show_alignment_functions = False
         self._functions = None
-        self._model = QFunctionTableModel(self.workspace, [])
+        self._model = QFunctionTableModel(self.instance, [])
 
         self.setModel(self._model)
 
@@ -319,7 +319,7 @@ def keyPressEvent(self, key_event):
 
     def contextMenuEvent(self, event:'PySide2.QtGui.QContextMenuEvent') -> None: # pylint:disable=unused-argument
         rows = self.selectionModel().selectedRows()
-        funcs = [self.workspace.instance.kb.functions[r.data()] for r in rows]
+        funcs = [self.instance.kb.functions[r.data()] for r in rows]
         self._context_menu.set(funcs).qmenu().popup(QCursor.pos())
 
 
@@ -353,9 +353,9 @@ class QFunctionTable(QWidget):
     Implements a table for listing function details.
     """
 
-    def __init__(self, parent, workspace, selection_callback=None):
+    def __init__(self, parent, instance, selection_callback=None):
         super().__init__(parent)
-        self.workspace = workspace
+        self.instance = instance
 
         self._view: 'FunctionsView' = parent
         self._table_view = None  # type: QFunctionTableView
@@ -449,7 +449,7 @@ def filter_functions(self, text):
     def _init_widgets(self, selection_callback=None):
 
         # function table view
-        self._table_view = QFunctionTableView(self, self.workspace, selection_callback)
+        self._table_view = QFunctionTableView(self, self.instance, selection_callback)
 
         # filter text box
         self._filter_box = QFunctionTableFilterBox(self)
diff --git a/angrmanagement/ui/widgets/qinst_annotation.py b/angrmanagement/ui/widgets/qinst_annotation.py
index 06003a74f..cfa6202e9 100644
--- a/angrmanagement/ui/widgets/qinst_annotation.py
+++ b/angrmanagement/ui/widgets/qinst_annotation.py
@@ -28,7 +28,7 @@ def disasm_view(self) -> 'DisassemblyView':
 
     @property
     def symexec_view(self) -> 'SymexecView':
-        return self.parentItem().disasm_view.workspace.view_manager.first_view_in_category("symexec")
+        return self.parentItem().disasm_view.instance.workspace.view_manager.first_view_in_category("symexec")
 
     def __init__(self, addr, text, *args, **kwargs):
         super().__init__(text, *args, **kwargs)
@@ -102,7 +102,7 @@ def _select_states():
             symexec_view = self.symexec_view
             if symexec_view:
                 symexec_view.select_states(self.states)
-                symexec_view.workspace.raise_view(symexec_view)
+                symexec_view.instance.workspace.raise_view(symexec_view)
 
         def _move_states():
             disasm_view = self.disasm_view
@@ -154,7 +154,7 @@ def contextMenuEvent(self, event): #pylint: disable=unused-argument
         menu.exec_(QCursor.pos())
 
     def delete(self):
-        self.disasm_view.workspace.instance.delete_hook(self.addr)
+        self.disasm_view.instance.delete_hook(self.addr)
         self.disasm_view.refresh()
 
 
@@ -213,7 +213,7 @@ def contextMenuEvent(self, event): #pylint: disable=unused-argument
         menu.exec_(QCursor.pos())
 
     def delete(self):
-        self.disasm_view.workspace.instance.breakpoint_mgr.remove_breakpoint(self.bp)
+        self.disasm_view.instance.breakpoint_mgr.remove_breakpoint(self.bp)
 
 
 class QAvoidAddrAnnotation(QExploreAnnotation):
diff --git a/angrmanagement/ui/widgets/qinstruction.py b/angrmanagement/ui/widgets/qinstruction.py
index bfd505c52..b68f93f8d 100644
--- a/angrmanagement/ui/widgets/qinstruction.py
+++ b/angrmanagement/ui/widgets/qinstruction.py
@@ -26,11 +26,11 @@ class QInstruction(QCachedGraphicsItem):
     LINEAR_INSTRUCTION_OFFSET = 120
     COMMENT_PREFIX = "// "
 
-    def __init__(self, workspace, func_addr, disasm_view, disasm, infodock, insn, out_branch, config, parent=None):
+    def __init__(self, instance, func_addr, disasm_view, disasm, infodock, insn, out_branch, config, parent=None):
         super().__init__(parent=parent)
 
         # initialization
-        self.workspace = workspace
+        self.instance = instance
         self.func_addr = func_addr
         self.disasm_view = disasm_view
         self.disasm = disasm
@@ -61,7 +61,7 @@ def contextMenuEvent(self, event:PySide2.QtWidgets.QGraphicsSceneContextMenuEven
         pass
 
     def mousePressEvent(self, event: QGraphicsSceneMouseEvent):
-        if self.workspace.plugins.handle_click_insn(self, event):
+        if self.instance.workspace.plugins.handle_click_insn(self, event):
             # stop handling this event if the event has been handled by a plugin
             event.accept()
         elif event.button() == Qt.LeftButton and QApplication.keyboardModifiers() in (Qt.NoModifier, Qt.ControlModifier):
@@ -85,7 +85,7 @@ def addr(self):
 
     def _calc_backcolor(self):
         # First we'll check for customizations
-        color = self.workspace.plugins.color_insn(self.insn.addr, self.selected)
+        color = self.instance.workspace.plugins.color_insn(self.insn.addr, self.selected)
         if color is not None:
             return color
 
@@ -126,10 +126,10 @@ def get_operand(self, operand_idx):
         return None
 
     def load_comment(self):
-        if self.workspace.instance.kb is None:
+        if self.instance.kb is None:
             self._comment = None
         else:
-            self._comment = get_comment_for_display(self.workspace.instance.kb, self.insn.addr)
+            self._comment = get_comment_for_display(self.instance.kb, self.insn.addr)
 
     def paint(self, painter, option, widget):  # pylint: disable=unused-argument
 
@@ -144,7 +144,7 @@ def paint(self, painter, option, widget):  # pylint: disable=unused-argument
             painter.drawRect(0, 0, self.width, self.height)
 
         # any plugin instruction rendering passes
-        self.workspace.plugins.draw_insn(self, painter)
+        self.instance.workspace.plugins.draw_insn(self, painter)
 
     #
     # Private methods
@@ -181,7 +181,7 @@ def _init_widgets(self):
                     # it does not create multiple branches. e.g., a call instruction
                     if len(operand.children) == 1 and type(operand.children[0]) is Value:
                         branch_targets = (operand.children[0].val,)
-            qoperand = QOperand(self.workspace, self.func_addr, self.disasm_view, self.disasm, self.infodock,
+            qoperand = QOperand(self.instance, self.func_addr, self.disasm_view, self.disasm, self.infodock,
                                 self.insn, operand, i, is_branch_target, is_indirect_branch, branch_targets,
                                 self._config, parent=self)
             self._operands.append(qoperand)
@@ -193,10 +193,10 @@ def _init_widgets(self):
             comma.setBrush(self._config.disasm_view_node_mnemonic_color)
             self._commas.append(comma)
 
-        if should_display_string_label(self.workspace.instance.cfg, self.insn.addr, self.workspace.instance.project):
+        if should_display_string_label(self.instance.cfg, self.insn.addr, self.instance.project):
             # yes we should display a string label
-            self._string = get_string_for_display(self.workspace.instance.cfg, self.insn.addr,
-                                                  self.workspace.instance.project)
+            self._string = get_string_for_display(self.instance.cfg, self.insn.addr,
+                                                  self.instance.project)
             if self._string is None:
                 self._string = "<Unknown>"
 
diff --git a/angrmanagement/ui/widgets/qlinear_viewer.py b/angrmanagement/ui/widgets/qlinear_viewer.py
index cb3f65fd2..f90522968 100644
--- a/angrmanagement/ui/widgets/qlinear_viewer.py
+++ b/angrmanagement/ui/widgets/qlinear_viewer.py
@@ -62,8 +62,8 @@ def event(self, event):
 class QLinearDisassembly(QDisassemblyBaseControl, QAbstractScrollArea):
     OBJECT_PADDING = 0
 
-    def __init__(self, workspace, disasm_view, parent=None):
-        QDisassemblyBaseControl.__init__(self, workspace, disasm_view, QAbstractScrollArea)
+    def __init__(self, instance, disasm_view, parent=None):
+        QDisassemblyBaseControl.__init__(self, instance, disasm_view, QAbstractScrollArea)
         QAbstractScrollArea.__init__(self, parent=parent)
 
         self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOn)
@@ -123,11 +123,11 @@ def max_offset(self):
 
     @property
     def cfg(self):
-        return self.workspace.instance.cfg
+        return self.instance.cfg
 
     @property
     def cfb(self):
-        return self.workspace.instance.cfb
+        return self.instance.cfb
 
     @property
     def scene(self):
@@ -259,7 +259,6 @@ def show_instruction(self, insn_addr, insn_pos=None, centering=False, use_block_
         :param use_block_pos:
         :return:
         """
-
         if insn_pos is not None:
             # check if item is already visible in the viewport
             viewport = self._viewer.viewport()
@@ -338,7 +337,6 @@ def prepare_objects(self, offset, start_line=0):
         _l.debug("Address %#x, offset %d, start_line %d.", addr, offset, start_line)
 
         self._insaddr_to_block.clear()
-
         if start_line < 0:
             # Which object are we currently displaying at the top of the disassembly view?
             try:
@@ -439,8 +437,8 @@ def _obj_to_paintable(self, obj_addr, obj):
             cfg_node = self.cfg.get_any_node(obj_addr, force_fastpath=True)
             if cfg_node is not None:
                 func_addr = cfg_node.function_address
-                if self.workspace.instance.kb.functions.contains_addr(func_addr):
-                    func = self.workspace.instance.kb.functions[func_addr]
+                if self.instance.kb.functions.contains_addr(func_addr):
+                    func = self.instance.kb.functions[func_addr]
                     disasm = self._get_disasm(func)
                     qobject = None
                     if self._disassembly_level is DisassemblyLevel.AIL:
@@ -452,11 +450,11 @@ def _obj_to_paintable(self, obj_addr, obj):
                                     ail_obj = n
                             # the corresponding AIL block may not exist
                             if ail_obj is not None:
-                                qobject = QLinearBlock(self.workspace, func_addr, self.disasm_view, disasm,
+                                qobject = QLinearBlock(self.instance, func_addr, self.disasm_view, disasm,
                                                        self.disasm_view.infodock, obj.addr, ail_obj, None, None
                                                        )
                     else:
-                        qobject = QLinearBlock(self.workspace, func_addr, self.disasm_view, disasm,
+                        qobject = QLinearBlock(self.instance, func_addr, self.disasm_view, disasm,
                                                self.disasm_view.infodock, obj.addr, [obj], {}, None
                                                )
                 else:
@@ -468,9 +466,9 @@ def _obj_to_paintable(self, obj_addr, obj):
                 _l.warning("Failed to get a CFG node for address %#x.", obj_addr)
                 qobject = None
         elif isinstance(obj, MemoryData):
-            qobject = QMemoryDataBlock(self.workspace, self.disasm_view.infodock, obj_addr, obj, parent=None)
+            qobject = QMemoryDataBlock(self.instance, self.disasm_view.infodock, obj_addr, obj, parent=None)
         elif isinstance(obj, Unknown):
-            qobject = QUnknownBlock(self.workspace, obj_addr, obj.bytes)
+            qobject = QUnknownBlock(self.instance, obj_addr, obj.bytes)
         else:
             qobject = None
         return qobject
@@ -499,10 +497,10 @@ def _get_disasm(self, func: Function) -> Optional[Union[Clinic, Disassembly]]:
         """
         if self._disassembly_level is DisassemblyLevel.AIL:
             if func.addr not in self._ail_disasms:
-                self._ail_disasms[func.addr] = self.workspace.instance.project.analyses.Clinic(func)
+                self._ail_disasms[func.addr] = self.instance.project.analyses.Clinic(func)
             return self._ail_disasms[func.addr]
 
         if func.addr not in self._disasms:
             include_ir = self._disassembly_level is DisassemblyLevel.LifterIR
-            self._disasms[func.addr] = self.workspace.instance.project.analyses.Disassembly(function=func, include_ir=include_ir)
+            self._disasms[func.addr] = self.instance.project.analyses.Disassembly(function=func, include_ir=include_ir)
         return self._disasms[func.addr]
diff --git a/angrmanagement/ui/widgets/qlog_widget.py b/angrmanagement/ui/widgets/qlog_widget.py
index 02cc30ee0..d575eae68 100644
--- a/angrmanagement/ui/widgets/qlog_widget.py
+++ b/angrmanagement/ui/widgets/qlog_widget.py
@@ -156,15 +156,15 @@ def __init__(self, log_view, *args, **kwargs):
         self.setColumnWidth(0, 20)
         hheader.setSectionResizeMode(1, QHeaderView.ResizeToContents)
 
-        self.log_view.workspace.instance.log.am_subscribe(self._on_new_logrecord)
+        self.log_view.instance.log.am_subscribe(self._on_new_logrecord)
 
     #
     # Public methods
     #
 
     def clear_log(self):
-        self.log_view.workspace.instance.log.am_obj = [ ]
-        self.log_view.workspace.instance.log.am_event()
+        self.log_view.instance.log.am_obj = [ ]
+        self.log_view.instance.log.am_event()
 
     def copy_selected(self):
         content = [ ]
@@ -206,7 +206,7 @@ def copy_all_messages(self):
     #
 
     def closeEvent(self, event):
-        self.log_view.workspace.instance.log.am_unsubscribe(self._on_new_logrecord)
+        self.log_view.instance.log.am_unsubscribe(self._on_new_logrecord)
         super().closeEvent(event)
 
     def contextMenuEvent(self, arg__1: PySide2.QtGui.QContextMenuEvent):
@@ -221,7 +221,7 @@ def _on_new_logrecord_core(self, log_record: LogRecord = None):
         if log_record is None:
             # reload
             self.model.layoutAboutToBeChanged.emit()
-            self.model._log = self.log_view.workspace.instance.log[::]
+            self.model._log = self.log_view.instance.log[::]
             self.model.layoutChanged.emit()
         else:
             log_records = len(self.model.log)
diff --git a/angrmanagement/ui/widgets/qmemory_data_block.py b/angrmanagement/ui/widgets/qmemory_data_block.py
index 46a5e5fe4..9eec7dc4e 100644
--- a/angrmanagement/ui/widgets/qmemory_data_block.py
+++ b/angrmanagement/ui/widgets/qmemory_data_block.py
@@ -17,9 +17,9 @@ class QMemoryDataBlock(QCachedGraphicsItem):
     LINEAR_INSTRUCTION_OFFSET = 120
     BYTE_AREA_SPACING = 15
 
-    def __init__(self, workspace, infodock, addr, memory_data, bytes_per_line=16, parent=None):
+    def __init__(self, instance, infodock, addr, memory_data, bytes_per_line=16, parent=None):
         super().__init__(parent=parent)
-        self.workspace = workspace
+        self.instance = instance
         self.infodock = infodock
         self.addr = addr
         self.memory_data: MemoryData = memory_data
@@ -91,7 +91,7 @@ def _init_widgets(self):
             start_address = self.memory_data.addr + len(self._bytes)
             size = self.memory_data.size - len(self._bytes)
             try:
-                mem_bytes = self.workspace.instance.project.loader.memory.load(start_address, size)
+                mem_bytes = self.instance.project.loader.memory.load(start_address, size)
             except KeyError:
                 mem_bytes = b""
             self._bytes += [ b for b in mem_bytes ] + [ '??' ] * (size - len(mem_bytes))
@@ -110,7 +110,7 @@ def _init_widgets(self):
         self._layout_items_and_update_size()
 
     def _init_label_item(self):
-        lbl_text = get_label_text(self.addr, self.workspace.instance.kb)
+        lbl_text = get_label_text(self.addr, self.instance.kb)
         if lbl_text:
             self._label_item = QGraphicsSimpleTextItem(lbl_text, self)
             self._label_item.setFont(Conf.code_font)
diff --git a/angrmanagement/ui/widgets/qmemory_viewer.py b/angrmanagement/ui/widgets/qmemory_viewer.py
index 3765ae0bf..5eccc40ee 100644
--- a/angrmanagement/ui/widgets/qmemory_viewer.py
+++ b/angrmanagement/ui/widgets/qmemory_viewer.py
@@ -23,9 +23,9 @@ class NewLinePiece(object):
 
 
 class QMemoryView(QWidget):
-    def __init__(self, state, workspace, parent=None):
+    def __init__(self, state, instance, parent=None):
         super(QMemoryView, self).__init__(parent)
-        self.workspace = workspace
+        self.instance = instance
 
         self.state = state
         self.cols = None
@@ -108,7 +108,7 @@ def _reload_objects(self):
             # QASTViewer objects
             for col in range(self.cols):
                 data = self.state.memory.load(addr + col, 1, inspect=False, disable_actions=True)
-                ast_viewer = QASTViewer(data, workspace=self.workspace, custom_painting=True, display_size=False)
+                ast_viewer = QASTViewer(data, workspace=self.instance.workspace, custom_painting=True, display_size=False)
                 objects.append(ast_viewer)
 
             # end of the line
diff --git a/angrmanagement/ui/widgets/qoperand.py b/angrmanagement/ui/widgets/qoperand.py
index bed17b45e..e5b95f167 100644
--- a/angrmanagement/ui/widgets/qoperand.py
+++ b/angrmanagement/ui/widgets/qoperand.py
@@ -18,11 +18,11 @@ class QOperand(QCachedGraphicsItem):
     LABEL_VARIABLE_SPACING = 5
     VARIABLE_IDENT_SPACING = 5
 
-    def __init__(self, workspace, func_addr, disasm_view, disasm, infodock, insn, operand, operand_index,
+    def __init__(self, instance, func_addr, disasm_view, disasm, infodock, insn, operand, operand_index,
                  is_branch_target, is_indirect_branch, branch_targets, config, parent=None):
         super().__init__(parent=parent)
 
-        self.workspace = workspace
+        self.instance = instance
         self.func_addr = func_addr
         self.disasm_view = disasm_view
         self.disasm = disasm
@@ -408,7 +408,7 @@ def _pick_variable(self, variable_and_offsets):
                 return None, None
 
             reg_name = the_reg.reg
-            arch = self.workspace.instance.project.arch
+            arch = self.instance.project.arch
 
             if len(variable_and_offsets) == 1:
                 # only one candidate...
diff --git a/angrmanagement/ui/widgets/qphivariable.py b/angrmanagement/ui/widgets/qphivariable.py
index 9c674a07d..88a93c626 100644
--- a/angrmanagement/ui/widgets/qphivariable.py
+++ b/angrmanagement/ui/widgets/qphivariable.py
@@ -11,7 +11,7 @@ class QPhiVariable(QCachedGraphicsItem):
 
     IDENT_LEFT_PADDING = 5
 
-    def __init__(self, workspace, disasm_view, phi_variable, config, parent=None):
+    def __init__(self, instance, disasm_view, phi_variable, config, parent=None):
         """
 
         :param workspace:
@@ -23,7 +23,7 @@ def __init__(self, workspace, disasm_view, phi_variable, config, parent=None):
         super().__init__(parent=parent)
 
         # initialization
-        self.workspace = workspace
+        self.instance = instance
         self.disasm_view = disasm_view
         self.phi = phi_variable.variable  # the major variable
         self.variables = phi_variable.variables  # the sub variables
diff --git a/angrmanagement/ui/widgets/qproximity_graph.py b/angrmanagement/ui/widgets/qproximity_graph.py
index 6b35805d4..50671e8b8 100644
--- a/angrmanagement/ui/widgets/qproximity_graph.py
+++ b/angrmanagement/ui/widgets/qproximity_graph.py
@@ -11,7 +11,6 @@
 from .qgraph_arrow import QProximityGraphArrow
 
 if TYPE_CHECKING:
-    from angrmanagement.ui.workspace import Workspace
     from angrmanagement.ui.views.proximity_view import ProximityView
     from angrmanagement.ui.widgets.qproximitygraph_block import QProximityGraphBlock
 
@@ -24,10 +23,10 @@ class QProximityGraph(QZoomableDraggableGraphicsView):
     LEFT_PADDING = 2000
     TOP_PADDING = 2000
 
-    def __init__(self, workspace: 'Workspace', proximity_view: 'ProximityView', parent=None):
+    def __init__(self, instance, proximity_view: 'ProximityView', parent=None):
         super().__init__(parent=parent)
 
-        self._workspace = workspace
+        self._instance = instance
         self._proximity_view = proximity_view
 
         self._graph = None
diff --git a/angrmanagement/ui/widgets/qstate_block.py b/angrmanagement/ui/widgets/qstate_block.py
index 787850f6f..4e3718925 100644
--- a/angrmanagement/ui/widgets/qstate_block.py
+++ b/angrmanagement/ui/widgets/qstate_block.py
@@ -20,7 +20,7 @@ def __init__(self, is_selected, symexec_view, state=None, history=None):
         super(QStateBlock, self).__init__()
 
         self.symexec_view = symexec_view
-        self._workspace = self.symexec_view.workspace
+        self._instance = self.symexec_view.instance
         self._config = Conf
 
         self.state = state
@@ -63,11 +63,11 @@ def _init_widgets(self):
         if addr is None:
             self._function_str = "Unknown"
         else:
-            the_func = locate_function(self._workspace.instance, addr)
+            the_func = locate_function(self._instance, addr)
             if the_func is None:
                 # is it a SimProcedure?
-                if self._workspace.instance.project.is_hooked(addr):
-                    hooker = self._workspace.instance.project.hooked_by(addr)
+                if self._instance.project.is_hooked(addr):
+                    hooker = self._instance.project.hooked_by(addr)
                     self._function_str = "SimProcedure " + hooker.__class__.__name__.split('.')[-1]
                 else:
                     self._function_str = "Unknown"
@@ -95,10 +95,10 @@ def mouseDoubleClickEvent(self, event):
         # _l.debug('QStateBlock received mouse double click event')
         if event.button() == Qt.LeftButton:
             if self.state is not None:
-                self._workspace.viz(self.state.addr)
+                self._instance.workspace.viz(self.state.addr)
                 event.accept()
             elif self.history is not None:
-                self._workspace.viz(self.history.state.addr)
+                self._instance.workspace.viz(self.history.state.addr)
                 event.accept()
 
         super().mouseDoubleClickEvent(event)
diff --git a/angrmanagement/ui/widgets/qtrace_map.py b/angrmanagement/ui/widgets/qtrace_map.py
index 39d0d57e0..127c419c8 100644
--- a/angrmanagement/ui/widgets/qtrace_map.py
+++ b/angrmanagement/ui/widgets/qtrace_map.py
@@ -309,6 +309,7 @@ class QTraceMapView(QGraphicsView):
     """
     Graphics view for trace map scene. The scene will rotate based on dimensions of the viewport to support horizontal
     and vertical orientations.
+    TODO: decouple this from the workspace.instance (main_instance)
     """
 
     def __init__(self, workspace, parent=None):
diff --git a/angrmanagement/ui/widgets/qvariable.py b/angrmanagement/ui/widgets/qvariable.py
index ec927775c..008b2a2b3 100644
--- a/angrmanagement/ui/widgets/qvariable.py
+++ b/angrmanagement/ui/widgets/qvariable.py
@@ -9,11 +9,11 @@ class QVariable(QCachedGraphicsItem):
     IDENT_LEFT_PADDING = 5
     OFFSET_LEFT_PADDING = 12
 
-    def __init__(self, workspace, disasm_view, variable, config, parent=None):
+    def __init__(self, instance, disasm_view, variable, config, parent=None):
         super(QVariable, self).__init__(parent=parent)
 
         # initialization
-        self.workspace = workspace
+        self.instance = instance
         self.disasm_view = disasm_view
         self.variable = variable
         self._config = config
diff --git a/angrmanagement/ui/widgets/qxref_viewer.py b/angrmanagement/ui/widgets/qxref_viewer.py
index f49b70010..4a4812344 100644
--- a/angrmanagement/ui/widgets/qxref_viewer.py
+++ b/angrmanagement/ui/widgets/qxref_viewer.py
@@ -325,12 +325,12 @@ def _reload(self):
     def _xrefs_from_control_flow_transitions(self):
 
         if self._disassembly_view is not None:
-            cfg = self._disassembly_view.workspace.instance.cfg
+            cfg = self._disassembly_view.instance.cfg
             node = cfg.get_any_node(self._dst_addr)
             if node is not None:
                 # its predecessors
                 predecessors = cfg.get_predecessors(node)
-                arch = self._disassembly_view.workspace.instance.project.arch
+                arch = self._disassembly_view.instance.project.arch
                 for pred in predecessors:
                     if pred.instruction_addrs:
                         if arch.branch_delay_slot and len(pred.instruction_addrs) > 1:
diff --git a/angrmanagement/ui/workspace.py b/angrmanagement/ui/workspace.py
index bb69657fa..4269f8c89 100644
--- a/angrmanagement/ui/workspace.py
+++ b/angrmanagement/ui/workspace.py
@@ -48,7 +48,7 @@ class Workspace:
     def __init__(self, main_window, instance):
 
         self.main_window: 'MainWindow' = main_window
-        self._instance = instance
+        self._main_instance = instance
         instance.workspace = self
 
         self.view_manager: ViewManager = ViewManager(self)
@@ -58,24 +58,24 @@ def __init__(self, main_window, instance):
         self.current_screen = ObjectContainer(None, name="current_screen")
 
         self.default_tabs = [
-            FunctionsView(self, 'left'),
-            DisassemblyView(self, 'center'),
-            HexView(self, 'center'),
-            ProximityView(self, 'center'),
-            CodeView(self, 'center'),
+            FunctionsView(self._main_instance, 'left'),
+            DisassemblyView(self._main_instance, 'center'),
+            HexView(self._main_instance, 'center'),
+            ProximityView(self._main_instance, 'center'),
+            CodeView(self._main_instance, 'center'),
         ]
         if Conf.has_operation_mango:
             self.default_tabs.append(
-                DependencyView(self, 'center')
+                DependencyView(self._main_instance, 'center')
             )
         self.default_tabs += [
-            StringsView(self, 'center'),
-            PatchesView(self, 'center'),
-            SymexecView(self, 'center'),
-            StatesView(self, 'center'),
-            InteractionView(self, 'center'),
-            ConsoleView(self, 'bottom'),
-            LogView(self, 'bottom'),
+            StringsView(self._main_instance, 'center'),
+            PatchesView(self._main_instance, 'center'),
+            SymexecView(self._main_instance, 'center'),
+            StatesView(self._main_instance, 'center'),
+            InteractionView(self._main_instance, 'center'),
+            ConsoleView(self._main_instance, 'bottom'),
+            LogView(self._main_instance, 'bottom'),
         ]
 
         enabled_tabs = [x.strip() for x in Conf.enabled_tabs.split(",") if x.strip()]
@@ -83,7 +83,7 @@ def __init__(self, main_window, instance):
             if tab.__class__.__name__ in enabled_tabs or len(enabled_tabs)==0:
                 self.add_view(tab)
 
-        self._dbg_watcher = DebuggerWatcher(self.on_debugger_state_updated, self.instance.debugger_mgr.debugger)
+        self._dbg_watcher = DebuggerWatcher(self.on_debugger_state_updated, self.main_instance.debugger_mgr.debugger)
         self.on_debugger_state_updated()
 
         self._analysis_configuration: Optional[AnalysesConfiguration] = None
@@ -97,8 +97,8 @@ def _main_window(self) -> 'MainWindow':
         return self.main_window
 
     @property
-    def instance(self) -> 'Instance':
-        return self._instance
+    def main_instance(self) -> 'Instance':
+        return self._main_instance
 
     #
     # Events
@@ -122,146 +122,13 @@ def on_debugger_state_updated(self):
                     view.jump_to(addr, True)
 
     def on_function_selected(self, func: Function):
-        """
-        Callback function triggered when a new function is selected in the function view.
-
-        :param func:    The function that is selected.
-        :return:        None
-        """
-
-        # Ask all current views to display this function
-
-        current_view = self.view_manager.current_tab
-        if current_view is None or not current_view.FUNCTION_SPECIFIC_VIEW:
-            # we don't have a current view or the current view does not have function-specific content. create a
-            # disassembly view to display the selected function.
-            disasm_view = self._get_or_create_disassembly_view()
-            disasm_view.display_function(func)
-            self.view_manager.raise_view(disasm_view)
-        else:
-            # ask the current view to display this function
-            current_view.function = func
-
-    def generate_cfg(self, cfg_args=None):
-        if cfg_args is None:
-            cfg_args = {}
-        cfg_job = CFGGenerationJob(
-            on_finish=self.on_cfg_generated,
-            **cfg_args
-        )
-        self.instance.add_job(cfg_job)
-        self.instance._start_daemon_thread(self._refresh_cfg, 'Progressively Refreshing CFG', args=(cfg_job,))
-
-    def _refresh_cfg(self, cfg_job):
-        """
-        Reload once and then refresh in a loop, while the CFG job is running
-        """
-        reloaded = False
-        while True:
-            if not self.instance.cfg.am_none:
-                if reloaded:
-                    gui_thread_schedule_async(self.refresh,
-                                              kwargs={'categories': ['disassembly', 'functions'],}
-                                              )
-                else:
-                    gui_thread_schedule_async(self.reload,
-                                              kwargs={'categories': ['disassembly', 'functions'],}
-                                              )
-                    reloaded = True
-
-            time.sleep(0.3)
-            if cfg_job not in self.instance.jobs:
-                break
-
-    def on_cfg_generated(self):
-        if self._analysis_configuration['flirt'].enabled:
-            self.instance.add_job(
-                FlirtSignatureRecognitionJob(
-                    on_finish=self._on_flirt_signature_recognized,
-                )
-            )
-
-        # display the main function if it exists, otherwise display the function at the entry point
-        if self.instance.cfg is not None:
-            the_func = self.instance.kb.functions.function(name='main')
-            if the_func is None:
-                the_func = self.instance.kb.functions.function(addr=self.instance.project.entry)
-
-            if the_func is not None:
-                self.on_function_selected(the_func)
-
-            # Initialize the linear viewer
-            if len(self.view_manager.views_by_category['disassembly']) == 1:
-                view = self.view_manager.first_view_in_category('disassembly')
-            else:
-                view = self.view_manager.current_view_in_category('disassembly')
-            if view is not None:
-                view._linear_viewer.initialize()
-
-            # Reload the pseudocode view
-            view = self.view_manager.first_view_in_category('pseudocode')
-            if view is not None:
-                view.reload()
-
-            # Reload the strings view
-            view = self.view_manager.first_view_in_category('strings')
-            if view is not None:
-                view.reload()
-
-            # Clear the proximity view
-            view = self.view_manager.first_view_in_category('proximity')
-            if view is not None:
-                view.clear()
-
-    def _on_flirt_signature_recognized(self):
-        self.instance.add_job(
-            PrototypeFindingJob(
-                on_finish=self._on_prototype_found,
-            )
-        )
-
-    def _on_prototype_found(self):
-        self.instance.add_job(
-            CodeTaggingJob(
-                on_finish=self.on_function_tagged,
-            )
-        )
-
-        if self._analysis_configuration['varec'].enabled:
-            workers = 4 if not is_testing else 0  # disable multiprocessing on angr CI
-            self.variable_recovery_job = VariableRecoveryJob(
-                **self._analysis_configuration['varec'].to_dict(),
-                on_variable_recovered=self.on_variable_recovered,
-                workers=workers,
-            )
-            # prioritize the current function in display
-            disassembly_view = self.view_manager.first_view_in_category("disassembly")
-            if disassembly_view is not None:
-                if not disassembly_view.function.am_none:
-                    self.variable_recovery_job.prioritize_function(disassembly_view.function.addr)
-            self.instance.add_job(self.variable_recovery_job)
+        self.main_instance.on_function_selected(func)
 
     def on_function_tagged(self):
-        # reload disassembly view
-        if len(self.view_manager.views_by_category['disassembly']) == 1:
-            view = self.view_manager.first_view_in_category('disassembly')
-        else:
-            view = self.view_manager.current_view_in_category('disassembly')
-
-        if view is not None:
-            view: DisassemblyView
-            if view.current_function.am_obj is not None:
-                view.reload()
+        self.main_instance.on_function_tagged()
 
     def on_variable_recovered(self, func_addr: int):
-        """
-        Called when variable information of the given function is available.
-
-        :param int func_addr:   Address of the function whose variable information is available.
-        """
-        disassembly_view = self.view_manager.first_view_in_category("disassembly")
-        if disassembly_view is not None:
-            disassembly_view.on_variable_recovered(func_addr)
+        self.main_instance.on_variable_recovered(func_addr)
 
     #
     # Public methods
@@ -277,7 +144,7 @@ def new_disassembly_view(self) -> DisassemblyView:
         else:
             current_addr = None
 
-        view = DisassemblyView(self, 'center')
+        view = DisassemblyView(self._main_instance, 'center')
         self.add_view(view)
         self.raise_view(view)
         view._linear_viewer.initialize()  # FIXME: Don't access protected member
@@ -357,7 +224,7 @@ def viz(self, obj):
         if type(obj) is int:
             self.jump_to(obj)
         elif type(obj) is str:
-            sym = self.instance.project.loader.find_symbol(obj)
+            sym = self.main_instance.project.loader.find_symbol(obj)
             if sym is not None:
                 self.jump_to(sym.rebased_addr)
         elif type(obj) is Function:
@@ -384,7 +251,7 @@ def add_breakpoint(self, obj: Union[str, int], type_: Optional[str] = None, size
         if type(obj) is int:
             addr = obj
         elif type(obj) is str:
-            sym = self.instance.project.loader.find_symbol(obj)
+            sym = self.main_instance.project.loader.find_symbol(obj)
             if sym is None:
                 _l.error("Couldn't resolve '%s'", obj)
                 return
@@ -419,24 +286,10 @@ def add_breakpoint(self, obj: Union[str, int], type_: Optional[str] = None, size
             return
 
         bp = Breakpoint(bp_type_map[type_], addr, size)
-        self.instance.breakpoint_mgr.add_breakpoint(bp)
+        self.main_instance.breakpoint_mgr.add_breakpoint(bp)
 
     def set_comment(self, addr, comment_text):
-        kb = self.instance.project.kb
-        exists = addr in kb.comments
-
-        # callback
-        if comment_text is None and exists:
-            self.plugins.handle_comment_changed(addr, "", False, False, False)
-            del kb.comments[addr]
-        else:
-            self.plugins.handle_comment_changed(addr, comment_text, not exists, False, False)
-            kb.comments[addr] = comment_text
-
-        # callback first
-        # TODO: can this be removed?
-        if self.instance.set_comment_callback:
-            self.instance.set_comment_callback(addr=addr, comment_text=comment_text)
+        self.main_instance.set_comment(addr, comment_text)
 
         disasm_view = self._get_or_create_disassembly_view()
         if disasm_view._flow_graph.disasm is not None:
@@ -444,31 +297,7 @@ def set_comment(self, addr, comment_text):
             disasm_view.current_graph.refresh()
 
     def run_analysis(self, prompt_for_configuration=True):
-        if self.instance.project.am_none:
-            return
-
-        if self._analysis_configuration is None:
-            self._analysis_configuration = AnalysesConfiguration([
-                a(self) for a in [
-                    CFGAnalysisConfiguration,
-                    FlirtAnalysisConfiguration,
-                    VariableRecoveryConfiguration
-                ]], self)
-
-        if not self.main_window.shown_at_start:
-            # If we are running headlessly (e.g. tests), just run with default configuration
-            prompt_for_configuration = False
-
-        if prompt_for_configuration:
-            dlg = AnalysisOptionsDialog(self._analysis_configuration, self, self.main_window)
-            dlg.setModal(True)
-            should_run = dlg.exec_()
-        else:
-            should_run = True
-
-        if should_run:
-            if self._analysis_configuration['cfg'].enabled:
-                self.generate_cfg(self._analysis_configuration['cfg'].to_dict())
+        self.main_instance.run_analysis(prompt_for_configuration=prompt_for_configuration)
 
     def decompile_current_function(self):
         current = self.view_manager.current_tab
@@ -512,7 +341,7 @@ def decompile_function(self, func: Function, curr_ins=None, view=None):
 
     def create_simulation_manager(self, state, state_name, view=None):
 
-        inst = self.instance
+        inst = self.main_instance
         hierarchy = StateHierarchy()
         simgr = inst.project.factory.simulation_manager(state, hierarchy=hierarchy)
         simgr_container = ObjectContainer(simgr, name=state_name)
@@ -526,28 +355,28 @@ def create_simulation_manager(self, state, state_name, view=None):
         self.raise_view(view)
 
     def create_trace_debugger(self):
-        if self.instance.current_trace.am_none:
+        if self.main_instance.current_trace.am_none:
             _l.error('No trace available')
             return
 
-        if isinstance(self.instance.current_trace.am_obj, BintraceTrace):
-            dbg = BintraceDebugger(self.instance.current_trace.am_obj, self)
+        if isinstance(self.main_instance.current_trace.am_obj, BintraceTrace):
+            dbg = BintraceDebugger(self.main_instance.current_trace.am_obj, self)
             dbg.init()
-            self.instance.debugger_list_mgr.add_debugger(dbg)
-            self.instance.debugger_mgr.set_debugger(dbg)
+            self.main_instance.debugger_list_mgr.add_debugger(dbg)
+            self.main_instance.debugger_mgr.set_debugger(dbg)
 
     def is_current_trace(self, trace: Optional[Trace]) -> bool:
-        return self.instance.current_trace.am_obj is trace
+        return self.main_instance.current_trace.am_obj is trace
 
     def set_current_trace(self, trace: Optional[Trace]):
-        self.instance.current_trace.am_obj = trace
-        self.instance.current_trace.am_event()
+        self.main_instance.current_trace.am_obj = trace
+        self.main_instance.current_trace.am_event()
 
     def remove_trace(self, trace: Trace):
         if self.is_current_trace(trace):
             self.set_current_trace(None)
-        self.instance.traces.remove(trace)
-        self.instance.traces.am_event(trace_removed=trace)
+        self.main_instance.traces.remove(trace)
+        self.main_instance.traces.am_event(trace_removed=trace)
         # Note: Debuggers and other objects may retain trace
 
     def load_trace_from_path(self, path: str):
@@ -559,14 +388,14 @@ def load_trace_from_path(self, path: str):
         trace = BintraceTrace.load_trace(path)
 
         def on_complete():
-            if not self.instance.project.am_none:
-                self.instance.traces.append(trace)
-                self.instance.traces.am_event(trace_added=trace)
-                self.instance.current_trace.am_obj = trace
-                self.instance.current_trace.am_event()
+            if not self.main_instance.project.am_none:
+                self.main_instance.traces.append(trace)
+                self.main_instance.traces.am_event(trace_added=trace)
+                self.main_instance.current_trace.am_obj = trace
+                self.main_instance.current_trace.am_event()
                 self.show_traces_view()
 
-        if self.instance.project.am_none:
+        if self.main_instance.project.am_none:
             QMessageBox.information(None, "Creating project", "Creating new project from trace.")
             self.create_project_from_trace(trace, on_complete)
         else:
@@ -596,10 +425,10 @@ def create_project_from_trace(self, trace: Trace, on_complete: Callable):
             on_complete()
             return
 
-        self.instance.binary_path = thing
-        self.instance.original_binary_path = thing
+        self.main_instance.binary_path = thing
+        self.main_instance.original_binary_path = thing
         job = LoadBinaryJob(thing, load_options=load_options, on_finish=on_complete)
-        self.instance.add_job(job)
+        self.main_instance.add_job(job)
 
     def interact_program(self, img_name, view=None):
         if view is None or view.category != 'interaction':
@@ -747,7 +576,7 @@ def show_log_view(self):
         view.setFocus()
 
     def toggle_exec_breakpoint(self):
-        if self.instance is None:
+        if self.main_instance is None:
             return
 
         view: Optional[DisassemblyView] = self.view_manager.first_view_in_category('disassembly')
@@ -755,19 +584,19 @@ def toggle_exec_breakpoint(self):
             selected_insns = view.current_graph.infodock.selected_insns
             if selected_insns:
                 for insn in selected_insns:
-                    self.instance.breakpoint_mgr.toggle_exec_breakpoint(insn)
+                    self.main_instance.breakpoint_mgr.toggle_exec_breakpoint(insn)
 
     def step_forward(self, until_addr: Optional[int] = None):
-        if self.instance is None:
+        if self.main_instance is None:
             return
 
-        self.instance.debugger_mgr.debugger.step_forward(until_addr=until_addr)
+        self.main_instance.debugger_mgr.debugger.step_forward(until_addr=until_addr)
 
     def continue_forward(self):
-        if self.instance is None:
+        if self.main_instance is None:
             return
 
-        self.instance.debugger_mgr.debugger.continue_forward()
+        self.main_instance.debugger_mgr.debugger.continue_forward()
 
     #
     # Private methods
@@ -778,7 +607,7 @@ def _get_or_create_disassembly_view(self) -> DisassemblyView:
         if view is None:
             view = self.view_manager.first_view_in_category('disassembly')
         if view is None:
-            view = DisassemblyView(self, 'center')
+            view = DisassemblyView(self._main_instance, 'center')
             self.add_view(view)
             view.reload()
 
@@ -788,7 +617,7 @@ def _create_hex_view(self) -> HexView:
         """
         Create a new hex view.
         """
-        view = HexView(self, 'center')
+        view = HexView(self._main_instance, 'center')
         self.add_view(view)
         return view
 
@@ -806,7 +635,7 @@ def _get_or_create_pseudocode_view(self):
 
         if view is None:
             # Create a new pseudo-code view
-            view = CodeView(self, 'center')
+            view = CodeView(self._main_instance, 'center')
             self.add_view(view)
 
         return view
@@ -817,7 +646,7 @@ def _get_or_create_symexec_view(self):
 
         if view is None:
             # Create a new symexec view
-            view = SymexecView(self, 'center')
+            view = SymexecView(self._main_instance, 'center')
             self.add_view(view)
 
         return view
@@ -828,7 +657,7 @@ def _get_or_create_states_view(self):
 
         if view is None:
             # Create a new states view
-            view = StatesView(self, 'center')
+            view = StatesView(self._main_instance, 'center')
             self.add_view(view)
 
         return view
@@ -839,7 +668,7 @@ def _get_or_create_strings_view(self):
 
         if view is None:
             # Create a new states view
-            view = StringsView(self, 'center')
+            view = StringsView(self._main_instance, 'center')
             self.add_view(view)
 
         return view
@@ -850,7 +679,7 @@ def _get_or_create_patches_view(self):
 
         if view is None:
             # Create a new states view
-            view = PatchesView(self, 'center')
+            view = PatchesView(self._main_instance, 'center')
             self.add_view(view)
 
         return view
@@ -859,7 +688,7 @@ def _get_or_create_interaction_view(self):
         view = self.view_manager.first_view_in_category("interaction")
         if view is None:
             # Create a new interaction view
-            view = InteractionView(self, 'center')
+            view = InteractionView(self._main_instance, 'center')
             self.add_view(view)
         return view
 
@@ -867,7 +696,7 @@ def _get_or_create_types_view(self):
         view = self.view_manager.first_view_in_category("types")
         if view is None:
             # Create a new interaction view
-            view = TypesView(self, 'center')
+            view = TypesView(self._main_instance, 'center')
             self.add_view(view)
         return view
 
@@ -877,7 +706,7 @@ def _get_or_create_data_dependency_graph(self, analysis_params: dict) -> Optiona
 
         if view is None:
             # Create a new data dependency view
-            view = DataDepView(self, 'center')
+            view = DataDepView(self._main_instance, 'center')
             self.add_view(view)
 
         # Update DataDepView to utilize new analysis params
@@ -891,7 +720,7 @@ def _get_or_create_proximity_view(self) -> ProximityView:
 
         if view is None:
             # Create a new proximity view
-            view = ProximityView(self, 'center')
+            view = ProximityView(self._main_instance, 'center')
             self.add_view(view)
 
         return view
@@ -902,7 +731,7 @@ def _get_or_create_console_view(self) -> ConsoleView:
 
         if view is None:
             # Create a new console view
-            view = ConsoleView(self, 'bottom')
+            view = ConsoleView(self._main_instance, 'bottom')
             self.add_view(view)
 
         return view
@@ -913,7 +742,7 @@ def _get_or_create_log_view(self) -> LogView:
 
         if view is None:
             # Create a new log view
-            view = LogView(self, 'bottom')
+            view = LogView(self._main_instance, 'bottom')
             self.add_view(view)
 
         return view
@@ -924,7 +753,7 @@ def _get_or_create_functions_view(self) -> FunctionsView:
 
         if view is None:
             # Create a new functions view
-            view = FunctionsView(self, 'left')
+            view = FunctionsView(self._main_instance, 'left')
             self.add_view(view)
 
         return view
@@ -934,7 +763,7 @@ def _get_or_create_registers_view(self) -> RegistersView:
         view = self.view_manager.first_view_in_category("registers")
 
         if view is None:
-            view = RegistersView(self, 'right')
+            view = RegistersView(self._main_instance, 'right')
             self.add_view(view)
 
         return view
@@ -944,7 +773,7 @@ def _get_or_create_stack_view(self) -> RegistersView:
         view = self.view_manager.first_view_in_category("stack")
 
         if view is None:
-            view = StackView(self, 'right')
+            view = StackView(self._main_instance, 'right')
             self.add_view(view)
 
         return view
@@ -954,7 +783,7 @@ def _get_or_create_traces_view(self) -> TracesView:
         view = self.view_manager.first_view_in_category("traces")
 
         if view is None:
-            view = TracesView(self, 'center')
+            view = TracesView(self._main_instance, 'center')
             self.add_view(view)
 
         return view
@@ -964,7 +793,7 @@ def _get_or_create_trace_map_view(self) -> TraceMapView:
         view = self.view_manager.first_view_in_category("tracemap")
 
         if view is None:
-            view = TraceMapView(self, 'top')
+            view = TraceMapView(self._main_instance, 'top')
             self.add_view(view)
 
         return view
@@ -974,7 +803,7 @@ def _get_or_create_breakpoints_view(self) -> BreakpointsView:
         view = self.view_manager.first_view_in_category("breakpoints")
 
         if view is None:
-            view = BreakpointsView(self, 'center')
+            view = BreakpointsView(self._main_instance, 'center')
             self.add_view(view)
 
         return view
@@ -984,7 +813,7 @@ def _get_or_create_call_explorer_view(self) -> CallExplorerView:
         view = self.view_manager.first_view_in_category('call_explorer')
 
         if view is None:
-            view = CallExplorerView(self, 'right')
+            view = CallExplorerView(self._main_instance, 'right')
             self.add_view(view)
 
         return view


Test patch:
diff --git a/tests/disabled_test_binsync_plugin.py b/tests/disabled_test_binsync_plugin.py
index 9a2aa5b98..a6445d843 100644
--- a/tests/disabled_test_binsync_plugin.py
+++ b/tests/disabled_test_binsync_plugin.py
@@ -34,10 +34,10 @@ def test_function_rename(self):
             # ====== USER 1 ======
             # setup GUI
             main = MainWindow(show=False)
-            main.workspace.instance.project.am_obj = angr.Project(binpath, auto_load_libs=False)
-            main.workspace.instance.project.am_event()
-            main.workspace.instance.join_all_jobs()
-            func = main.workspace.instance.project.kb.functions['main']
+            main.workspace.main_instance.project.am_obj = angr.Project(binpath, auto_load_libs=False)
+            main.workspace.main_instance.project.am_event()
+            main.workspace.main_instance.join_all_jobs()
+            func = main.workspace.main_instance.project.kb.functions['main']
             self.assertIsNotNone(func)
 
             # find the binsync plugin
@@ -47,7 +47,7 @@ def test_function_rename(self):
             ))  # type: BinsyncPlugin
 
             # configure, and connect
-            config = SyncConfig(main.workspace.instance, binsync_plugin.controller)
+            config = SyncConfig(main.workspace.main_instance, binsync_plugin.controller)
             config._user_edit.setText("")
             config._repo_edit.setText("")
             QTest.keyClicks(config._user_edit, user_1)
@@ -64,7 +64,7 @@ def test_function_rename(self):
             disasm_view._t_flow_graph_visible = True
             disasm_view.display_function(func)
             disasm_view.decompile_current_function()
-            main.workspace.instance.join_all_jobs()
+            main.workspace.main_instance.join_all_jobs()
             pseudocode_view = main.workspace._get_or_create_pseudocode_view()
             for _, item in pseudocode_view.codegen.map_pos_to_node.items():
                 if isinstance(item.obj, angr.analyses.decompiler.structured_codegen.c.CFunction):
@@ -87,10 +87,10 @@ def test_function_rename(self):
             # ====== USER 2 ======
             # setup GUI
             main = MainWindow(show=False)
-            main.workspace.instance.project.am_obj = angr.Project(binpath, auto_load_libs=False)
-            main.workspace.instance.project.am_event()
-            main.workspace.instance.join_all_jobs()
-            func = main.workspace.instance.project.kb.functions['main']
+            main.workspace.main_instance.project.am_obj = angr.Project(binpath, auto_load_libs=False)
+            main.workspace.main_instance.project.am_event()
+            main.workspace.main_instance.join_all_jobs()
+            func = main.workspace.main_instance.project.kb.functions['main']
             self.assertIsNotNone(func)
 
             # find the binsync plugin
@@ -100,7 +100,7 @@ def test_function_rename(self):
             ))  # type: BinsyncPlugin
 
             # configure, and connect
-            config = SyncConfig(main.workspace.instance, binsync_plugin.controller)
+            config = SyncConfig(main.workspace.main_instance, binsync_plugin.controller)
             config._user_edit.setText("")
             config._repo_edit.setText("")
             QTest.keyClicks(config._user_edit, user_2)
@@ -135,10 +135,10 @@ def test_stack_variable_rename(self):
             # ====== USER 1 ======
             # setup GUI
             main = MainWindow(show=False)
-            main.workspace.instance.project.am_obj = angr.Project(binpath, auto_load_libs=False)
-            main.workspace.instance.project.am_event()
-            main.workspace.instance.join_all_jobs()
-            func = main.workspace.instance.project.kb.functions['main']
+            main.workspace.main_instance.project.am_obj = angr.Project(binpath, auto_load_libs=False)
+            main.workspace.main_instance.project.am_event()
+            main.workspace.main_instance.join_all_jobs()
+            func = main.workspace.main_instance.project.kb.functions['main']
             self.assertIsNotNone(func)
 
             # find the binsync plugin
@@ -148,7 +148,7 @@ def test_stack_variable_rename(self):
             ))  # type: BinsyncPlugin
 
             # configure, and connect
-            config = SyncConfig(main.workspace.instance, binsync_plugin.controller)
+            config = SyncConfig(main.workspace.main_instance, binsync_plugin.controller)
             config._user_edit.setText("")
             config._repo_edit.setText("")
             QTest.keyClicks(config._user_edit, user_1)
@@ -165,7 +165,7 @@ def test_stack_variable_rename(self):
             disasm_view._t_flow_graph_visible = True
             disasm_view.display_function(func)
             disasm_view.decompile_current_function()
-            main.workspace.instance.join_all_jobs()
+            main.workspace.main_instance.join_all_jobs()
             pseudocode_view = main.workspace._get_or_create_pseudocode_view()
             for _, item in pseudocode_view.codegen.map_pos_to_node.items():
                 if isinstance(item.obj, angr.analyses.decompiler.structured_codegen.c.CVariable) and \
@@ -181,7 +181,7 @@ def test_stack_variable_rename(self):
             QTest.mouseClick(rnode._ok_button, Qt.MouseButton.LeftButton)
 
             # find the variable in the var manager
-            var_man = main.workspace.instance.pseudocode_variable_kb.variables.get_function_manager(func.addr)
+            var_man = main.workspace.main_instance.pseudocode_variable_kb.variables.get_function_manager(func.addr)
             for var in var_man._unified_variables:
                 if isinstance(var, angr.sim_variable.SimStackVariable) and var.offset == var_offset:
                     renamed_var = var
@@ -199,10 +199,10 @@ def test_stack_variable_rename(self):
             # ====== USER 2 ======
             # setup GUI
             main = MainWindow(show=False)
-            main.workspace.instance.project.am_obj = angr.Project(binpath, auto_load_libs=False)
-            main.workspace.instance.project.am_event()
-            main.workspace.instance.join_all_jobs()
-            func = main.workspace.instance.project.kb.functions['main']
+            main.workspace.main_instance.project.am_obj = angr.Project(binpath, auto_load_libs=False)
+            main.workspace.main_instance.project.am_event()
+            main.workspace.main_instance.join_all_jobs()
+            func = main.workspace.main_instance.project.kb.functions['main']
             self.assertIsNotNone(func)
 
             # find the binsync plugin
@@ -212,7 +212,7 @@ def test_stack_variable_rename(self):
             ))  # type: BinsyncPlugin
 
             # configure, and connect
-            config = SyncConfig(main.workspace.instance, binsync_plugin.controller)
+            config = SyncConfig(main.workspace.main_instance, binsync_plugin.controller)
             config._user_edit.setText("")
             config._repo_edit.setText("")
             QTest.keyClicks(config._user_edit, user_2)
diff --git a/tests/manual_human_activities.py b/tests/manual_human_activities.py
index 146be511e..18446cb54 100644
--- a/tests/manual_human_activities.py
+++ b/tests/manual_human_activities.py
@@ -39,12 +39,12 @@ def tearDown(self):
     def _open_a_project(self):
         main = MainWindow(show=False)
         binpath = os.path.join(test_location, "x86_64", "fauxware")
-        main.workspace.instance.project.am_obj = angr.Project(binpath, auto_load_libs=False)
-        main.workspace.instance.project.am_event()
-        main.workspace.instance.join_all_jobs()
+        main.workspace.main_instance.project.am_obj = angr.Project(binpath, auto_load_libs=False)
+        main.workspace.main_instance.project.am_event()
+        main.workspace.main_instance.join_all_jobs()
         self.project = binpath
         # import ipdb; ipdb.set_trace()
-        self.project_md5 = main.workspace.instance.project.loader.main_object.md5.hex()
+        self.project_md5 = main.workspace.main_instance.project.loader.main_object.md5.hex()
         return main
 
     def test_open_a_project(self):
@@ -53,7 +53,7 @@ def test_open_a_project(self):
     def test_rename_a_function_in_disasm_and_pseudocode_views(self):
         main = self._open_a_project()
 
-        func = main.workspace.instance.project.kb.functions['main']
+        func = main.workspace.main_instance.project.kb.functions['main']
         self.assertIsNotNone(func)
 
         # decompile the function
@@ -61,7 +61,7 @@ def test_rename_a_function_in_disasm_and_pseudocode_views(self):
         disasm_view._t_flow_graph_visible = True
         disasm_view.display_function(func)
         disasm_view.decompile_current_function()
-        main.workspace.instance.join_all_jobs()
+        main.workspace.main_instance.join_all_jobs()
         pseudocode_view = main.workspace._get_or_create_pseudocode_view()
 
         # find the node for function
@@ -105,7 +105,7 @@ def test_rename_a_function_in_disasm_and_pseudocode_views(self):
     def test_rename_a_variable_in_pseudocode_view(self):
         main = self._open_a_project()
 
-        func = main.workspace.instance.project.kb.functions['main']
+        func = main.workspace.main_instance.project.kb.functions['main']
         self.assertIsNotNone(func)
 
         # decompile the function
@@ -113,7 +113,7 @@ def test_rename_a_variable_in_pseudocode_view(self):
         disasm_view._t_flow_graph_visible = True
         disasm_view.display_function(func)
         disasm_view.decompile_current_function()
-        main.workspace.instance.join_all_jobs()
+        main.workspace.main_instance.join_all_jobs()
         pseudocode_view = main.workspace._get_or_create_pseudocode_view()
 
         # find an arbitrary node for a variable
@@ -144,7 +144,7 @@ def test_rename_a_variable_in_pseudocode_view(self):
 
     def test_click_block(self):
         main_window = self._open_a_project()
-        func = main_window.workspace.instance.project.kb.functions['main']
+        func = main_window.workspace.main_instance.project.kb.functions['main']
         self.assertIsNotNone(func)
 
         # display function main
@@ -171,7 +171,7 @@ def test_click_block(self):
 
     def test_click_insn(self):
         main_window = self._open_a_project()
-        func = main_window.workspace.instance.project.kb.functions['main']
+        func = main_window.workspace.main_instance.project.kb.functions['main']
         self.assertIsNotNone(func)
 
         # display function main
diff --git a/tests/test_qaddress_input.py b/tests/test_qaddress_input.py
index ecd7ceae5..097b4e380 100644
--- a/tests/test_qaddress_input.py
+++ b/tests/test_qaddress_input.py
@@ -18,10 +18,10 @@ def setUp(self):
 
     def test_address_conversion(self):
         main = MainWindow(show=False)
-        main.workspace.instance.project = load_shellcode(b'X', 'amd64')
-        main.workspace.instance.project.kb.functions.function(addr=0x1234, name='foo', create=True)
+        main.workspace.main_instance.project = load_shellcode(b'X', 'amd64')
+        main.workspace.main_instance.project.kb.functions.function(addr=0x1234, name='foo', create=True)
 
-        obj = QAddressInput(None, main.workspace)
+        obj = QAddressInput(None, main.workspace.main_instance)
 
         obj.setText("")
         QTest.keyClicks(obj, "4321")
@@ -39,10 +39,10 @@ def test_address_conversion(self):
     def test_function_name(self):
         proj = angr.Project(os.path.join(test_location, "x86_64", "fauxware"), auto_load_libs=False)
         main = MainWindow(show=False)
-        main.workspace.instance.project = proj
+        main.workspace.main_instance.project = proj
 
         cfg = proj.analyses.CFG()
-        obj = QAddressInput(None, main.workspace)
+        obj = QAddressInput(None, main.workspace.main_instance)
 
         obj.setText("")
         QTest.keyClicks(obj, "main")
diff --git a/tests/test_rename_functions.py b/tests/test_rename_functions.py
index a894f0c71..222db22fd 100644
--- a/tests/test_rename_functions.py
+++ b/tests/test_rename_functions.py
@@ -20,11 +20,11 @@ def setUp(self):
     def test_rename_a_function_in_disasm_and_pseudocode_views(self):
         main = MainWindow(show=False)
         binpath = os.path.join(test_location, "x86_64", "fauxware")
-        main.workspace.instance.project.am_obj = angr.Project(binpath, auto_load_libs=False)
-        main.workspace.instance.project.am_event()
-        main.workspace.instance.join_all_jobs()
+        main.workspace.main_instance.project.am_obj = angr.Project(binpath, auto_load_libs=False)
+        main.workspace.main_instance.project.am_event()
+        main.workspace.main_instance.join_all_jobs()
 
-        func = main.workspace.instance.project.kb.functions['main']
+        func = main.workspace.main_instance.project.kb.functions['main']
         self.assertIsNotNone(func)
 
         # decompile the function
@@ -32,7 +32,7 @@ def test_rename_a_function_in_disasm_and_pseudocode_views(self):
         disasm_view._t_flow_graph_visible = True
         disasm_view.display_function(func)
         disasm_view.decompile_current_function()
-        main.workspace.instance.join_all_jobs()
+        main.workspace.main_instance.join_all_jobs()
         pseudocode_view = main.workspace._get_or_create_pseudocode_view()
 
         # find the node for function
diff --git a/tests/test_workflow.py b/tests/test_workflow.py
index 572dc50f4..3551750aa 100644
--- a/tests/test_workflow.py
+++ b/tests/test_workflow.py
@@ -15,9 +15,9 @@ def setUp(self):
     def test_workflow(self):
         main = MainWindow(show=False)
         proj = angr.Project(os.path.join(test_location, "x86_64", "true"), auto_load_libs=False)
-        main.workspace.instance.project.am_obj = proj
-        main.workspace.instance.project.am_event()
-        main.workspace.instance.join_all_jobs()
+        main.workspace.main_instance.project.am_obj = proj
+        main.workspace.main_instance.project.am_event()
+        main.workspace.main_instance.join_all_jobs()
 
 
 if __name__ == '__main__':