brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.4 KiB · 6e89d44 Raw
174 lines · typescript
1import { DebugProtocol } from "@vscode/debugprotocol";2import * as vscode from "vscode";3 4export interface LLDBDapCapabilities extends DebugProtocol.Capabilities {5  /** The debug adapter supports the `moduleSymbols` request. */6  supportsModuleSymbolsRequest?: boolean;7}8 9/** A helper type for mapping event types to their corresponding data type. */10// prettier-ignore11interface EventMap {12  "module": DebugProtocol.ModuleEvent;13  "exited": DebugProtocol.ExitedEvent;14  "capabilities": DebugProtocol.CapabilitiesEvent;15}16 17/** A type assertion to check if a ProtocolMessage is an event or if it is a specific event. */18function isEvent(19  message: DebugProtocol.ProtocolMessage,20): message is DebugProtocol.Event;21function isEvent<K extends keyof EventMap>(22  message: DebugProtocol.ProtocolMessage,23  event: K,24): message is EventMap[K];25function isEvent(26  message: DebugProtocol.ProtocolMessage,27  event?: string,28): boolean {29  return (30    message.type === "event" &&31    (!event || (message as DebugProtocol.Event).event === event)32  );33}34 35/** Tracks lldb-dap sessions for data visualizers. */36export class DebugSessionTracker37  implements vscode.DebugAdapterTrackerFactory, vscode.Disposable38{39  /**40   * Tracks active modules for each debug sessions.41   *42   * The modules are kept in an array to maintain the load order of the modules.43   */44  private modules = new Map<vscode.DebugSession, DebugProtocol.Module[]>();45  private modulesChanged = new vscode.EventEmitter<46    vscode.DebugSession | undefined47  >();48  private sessionReceivedCapabilities =49      new vscode.EventEmitter<[ vscode.DebugSession, LLDBDapCapabilities ]>();50  private sessionExited = new vscode.EventEmitter<vscode.DebugSession>();51 52  /**53   * Fired when modules are changed for any active debug session.54   *55   * Use `debugSessionModules` to retieve the active modules for a given debug session.56   */57  onDidChangeModules: vscode.Event<vscode.DebugSession | undefined> =58    this.modulesChanged.event;59 60  /** Fired when a debug session is initialized. */61  onDidReceiveSessionCapabilities:62      vscode.Event<[ vscode.DebugSession, LLDBDapCapabilities ]> =63      this.sessionReceivedCapabilities.event;64 65  /** Fired when a debug session is exiting. */66  onDidExitSession: vscode.Event<vscode.DebugSession> =67    this.sessionExited.event;68 69  constructor(private logger: vscode.LogOutputChannel) {70    this.onDidChangeModules(this.moduleChangedListener, this);71    vscode.debug.onDidChangeActiveDebugSession((session) =>72      this.modulesChanged.fire(session),73    );74  }75 76  dispose() {77    this.modules.clear();78    this.modulesChanged.dispose();79  }80 81  createDebugAdapterTracker(82    session: vscode.DebugSession,83  ): vscode.ProviderResult<vscode.DebugAdapterTracker> {84    this.logger.info(`Starting debug session "${session.name}"`);85    let stopping = false;86    return {87      onError: (error) => !stopping && this.logger.error(error), // Can throw benign read errors when shutting down.88      onDidSendMessage: (message) => this.onDidSendMessage(session, message),89      onWillStopSession: () => (stopping = true),90      onExit: () => this.onExit(session),91    };92  }93 94  /**95   * Retrieves the modules for the given debug session.96   *97   * Modules are returned in load order.98   */99  debugSessionModules(session: vscode.DebugSession): DebugProtocol.Module[] {100    return this.modules.get(session) ?? [];101  }102 103  /** Clear information from the active session. */104  private onExit(session: vscode.DebugSession) {105    this.modules.delete(session);106    this.modulesChanged.fire(undefined);107  }108 109  private showModulesTreeView(showModules: boolean) {110    vscode.commands.executeCommand(111      "setContext",112      "lldb-dap.showModules",113      showModules,114    );115  }116 117  private moduleChangedListener(session: vscode.DebugSession | undefined) {118    if (!session) {119      this.showModulesTreeView(false);120      return;121    }122 123    if (session == vscode.debug.activeDebugSession) {124      const sessionHasModules = this.modules.get(session) != undefined;125      this.showModulesTreeView(sessionHasModules);126    }127  }128 129  private onDidSendMessage(130    session: vscode.DebugSession,131    message: DebugProtocol.ProtocolMessage,132  ) {133    if (isEvent(message, "module")) {134      const { module, reason } = message.body;135      const modules = this.modules.get(session) ?? [];136      switch (reason) {137        case "new":138        case "changed": {139          const index = modules.findIndex((m) => m.id === module.id);140          if (index !== -1) {141            modules[index] = module;142          } else {143            modules.push(module);144          }145          break;146        }147        case "removed": {148          const index = modules.findIndex((m) => m.id === module.id);149          if (index !== -1) {150            modules.splice(index, 1);151          }152          break;153        }154        default:155          console.error("unexpected module event reason");156          break;157      }158      this.modules.set(session, modules);159      this.modulesChanged.fire(session);160    } else if (isEvent(message, "exited")) {161      // The vscode.DebugAdapterTracker#onExit event is sometimes called with162      // exitCode = undefined but the exit event from LLDB-DAP always has the "exitCode"163      const { exitCode } = message.body;164      this.logger.info(165        `Session "${session.name}" exited with code ${exitCode}`,166      );167 168      this.sessionExited.fire(session);169    } else if (isEvent(message, "capabilities")) {170      this.sessionReceivedCapabilities.fire([ session, message.body.capabilities ]);171    }172  }173}174