(response: DebugProtocol.StackTraceResponse, args: DebugProtocol.StackTraceArguments)
| 644 | } |
| 645 | |
| 646 | protected async stackTraceRequest(response: DebugProtocol.StackTraceResponse, args: DebugProtocol.StackTraceArguments): Promise<void> { |
| 647 | const stackFrameBatch = 20; |
| 648 | const thread = this.threadManager.getThreadInfoFromNumber(args.threadId); |
| 649 | const startFrame = args.startFrame || 0; |
| 650 | const levels = args.levels; |
| 651 | |
| 652 | if (!thread) { |
| 653 | this.errorResponse(response, `No thread with id ${args.threadId}`); |
| 654 | return; |
| 655 | } |
| 656 | |
| 657 | if (!thread.paused) { |
| 658 | this.errorResponse(response, `Thread ${args.threadId} is not paused`); |
| 659 | return; |
| 660 | } |
| 661 | |
| 662 | if (!this.vmService) { |
| 663 | this.errorResponse(response, `No VM service connection`); |
| 664 | return; |
| 665 | } |
| 666 | |
| 667 | try { |
| 668 | let isTruncated: boolean; |
| 669 | let totalFrames: number; |
| 670 | let stackFrames: DebugProtocol.StackFrame[] = []; |
| 671 | |
| 672 | // Newer VM Service allows us to cap the frames to avoid fetching more than we need. |
| 673 | // They do not support an offset, however earlier frames will be internally cached |
| 674 | // so fetching 40 frames if we've already had 0-20 should only incur the cost of the |
| 675 | // new 20. |
| 676 | const supportsGetStackLimit = this.vmServiceCapabilities.supportsGetStackLimit; |
| 677 | const limit = supportsGetStackLimit && levels |
| 678 | ? startFrame + levels |
| 679 | : undefined; |
| 680 | |
| 681 | // If the request is only for the top frame, we may be able to satisfy this using the |
| 682 | // `topFrame` field of the pause event. |
| 683 | if (startFrame === 0 && levels === 1 && thread.pauseEvent?.topFrame) { |
| 684 | totalFrames = 1 + stackFrameBatch; // Claim we had more +stackFrameBatch frames to force another request. |
| 685 | stackFrames.push(await this.convertStackFrame(thread, thread.pauseEvent?.topFrame, true, true, Infinity)); |
| 686 | } else { |
| 687 | const result = await this.vmService.getStack(thread.ref.id, limit); |
| 688 | |
| 689 | const stack: VMStack = result.result as VMStack; |
| 690 | let vmFrames = stack.asyncCausalFrames || stack.frames; |
| 691 | const framesReceived = vmFrames.length; |
| 692 | isTruncated = stack.truncated ?? false; |
| 693 | |
| 694 | let firstAsyncMarkerIndex = vmFrames.findIndex((f) => f.kind === "AsyncSuspensionMarker"); |
| 695 | if (firstAsyncMarkerIndex === -1) |
| 696 | firstAsyncMarkerIndex = Infinity; |
| 697 | |
| 698 | // Drop frames that are earlier than what we wanted. |
| 699 | vmFrames = vmFrames.slice(startFrame); |
| 700 | |
| 701 | // Drop off any that are after where we wanted. |
| 702 | if (levels && vmFrames.length > levels) |
| 703 | vmFrames = vmFrames.slice(0, levels); |
nothing calls this directly
no test coverage detected