1 /* ---------------------------------------------------------------------------
3 * (c) The GHC Team, 2020-2021
5 * Stack snapshotting and decoding. (Cloning and unwinding.)
7 *---------------------------------------------------------------------------*/
11 #include "rts/Messages.h"
13 #include "rts/Types.h"
14 #include "rts/storage/TSO.h"
15 #include "stg/Types.h"
16 #include "CloneStack.h"
17 #include "StablePtr.h"
22 #include "sm/Sanity.h"
29 static StgWord
getStackFrameCount(StgStack
* stack
);
30 static StgWord
getStackChunkClosureCount(StgStack
* stack
);
31 static StgArrBytes
* allocateByteArray(Capability
*cap
, StgWord bytes
);
32 static void copyPtrsToArray(StgArrBytes
* arr
, StgStack
* stack
);
34 static StgStack
* cloneStackChunk(Capability
* capability
, const StgStack
* stack
)
36 StgWord spOffset
= stack
->sp
- stack
->stack
;
37 StgWord closureSizeBytes
= sizeof(StgStack
) + (stack
->stack_size
* sizeof(StgWord
));
39 StgStack
* newStackClosure
= (StgStack
*) allocate(capability
, ROUNDUP_BYTES_TO_WDS(closureSizeBytes
));
41 memcpy(newStackClosure
, stack
, closureSizeBytes
);
43 newStackClosure
->sp
= newStackClosure
->stack
+ spOffset
;
44 // The new stack is not on the mutable list; clear the dirty flag such that
45 // we don't claim that it is.
46 newStackClosure
->dirty
= 0;
49 checkClosure((StgClosure
*) newStackClosure
);
52 return newStackClosure
;
55 StgStack
* cloneStack(Capability
* capability
, const StgStack
* stack
)
57 StgStack
*top_stack
= cloneStackChunk(capability
, stack
);
58 StgStack
*last_stack
= top_stack
;
60 // check whether the stack ends in an underflow frame
61 StgUnderflowFrame
*frame
= (StgUnderflowFrame
*) (last_stack
->stack
62 + last_stack
->stack_size
- sizeofW(StgUnderflowFrame
));
63 if (frame
->info
== &stg_stack_underflow_frame_d_info
64 ||frame
->info
== &stg_stack_underflow_frame_v16_info
65 ||frame
->info
== &stg_stack_underflow_frame_v32_info
66 ||frame
->info
== &stg_stack_underflow_frame_v64_info
) {
67 StgStack
*s
= cloneStackChunk(capability
, frame
->next_chunk
);
68 frame
->next_chunk
= s
;
77 #if defined(THREADED_RTS)
79 // ThreadId# in Haskell is a StgTSO* in RTS.
80 void sendCloneStackMessage(StgTSO
*tso
, HsStablePtr mvar
) {
81 Capability
*srcCapability
= rts_unsafeGetMyCapability();
83 MessageCloneStack
*msg
;
84 msg
= (MessageCloneStack
*)allocate(srcCapability
, sizeofW(MessageCloneStack
));
86 msg
->result
= (StgMVar
*)deRefStablePtr(mvar
);
87 SET_HDR_RELEASE(msg
, &stg_MSG_CLONE_STACK_info
, CCS_SYSTEM
);
89 sendMessage(srcCapability
, tso
->cap
, (Message
*)msg
);
92 void handleCloneStackMessage(MessageCloneStack
*msg
){
93 StgStack
* newStackClosure
= cloneStack(msg
->tso
->cap
, msg
->tso
->stackobj
);
95 // Lift StackSnapshot# to StackSnapshot by applying it's constructor.
96 // This is necessary because performTryPutMVar() puts the closure onto the
97 // stack for evaluation and stacks can not be evaluated (entered).
98 HaskellObj result
= rts_apply(msg
->tso
->cap
, StackSnapshot_constructor_closure
, (HaskellObj
) newStackClosure
);
100 bool putMVarWasSuccessful
= performTryPutMVar(msg
->tso
->cap
, msg
->result
, result
);
102 if(!putMVarWasSuccessful
) {
103 barf("Can't put stack cloning result into MVar.");
107 #else // !defined(THREADED_RTS)
110 void sendCloneStackMessage(StgTSO
*tso STG_UNUSED
, HsStablePtr mvar STG_UNUSED
) {
111 barf("Sending CloneStackMessages is only available in threaded RTS!");
114 #endif // end !defined(THREADED_RTS)
116 // Creates a MutableArray# (Haskell representation) that contains a
117 // InfoProvEnt* for every stack frame on the given stack. Thus, the size of the
118 // array is the count of stack frames.
119 // Each InfoProvEnt* is looked up by lookupIPE(). If there's no IPE for a stack
120 // frame it's represented by null.
121 StgArrBytes
* decodeClonedStack(Capability
*cap
, StgStack
* stack
) {
122 StgWord closureCount
= getStackFrameCount(stack
);
124 StgArrBytes
* array
= allocateByteArray(cap
, sizeof(StgInfoTable
*) * closureCount
);
126 copyPtrsToArray(array
, stack
);
131 // Count the stack frames that are on the given stack.
132 // This is the sum of all stack frames in all stack chunks of this stack.
133 StgWord
getStackFrameCount(StgStack
* stack
) {
134 StgWord closureCount
= 0;
135 StgStack
*last_stack
= stack
;
137 closureCount
+= getStackChunkClosureCount(last_stack
);
139 // check whether the stack ends in an underflow frame
140 StgUnderflowFrame
*frame
= (StgUnderflowFrame
*) (last_stack
->stack
141 + last_stack
->stack_size
- sizeofW(StgUnderflowFrame
));
142 if (frame
->info
== &stg_stack_underflow_frame_d_info
143 ||frame
->info
== &stg_stack_underflow_frame_v16_info
144 ||frame
->info
== &stg_stack_underflow_frame_v32_info
145 ||frame
->info
== &stg_stack_underflow_frame_v64_info
) {
146 last_stack
= frame
->next_chunk
;
154 StgWord
getStackChunkClosureCount(StgStack
* stack
) {
155 StgWord closureCount
= 0;
156 StgPtr sp
= stack
->sp
;
157 StgPtr spBottom
= stack
->stack
+ stack
->stack_size
;
158 for (; sp
< spBottom
; sp
+= stack_frame_sizeW((StgClosure
*)sp
)) {
165 // Allocate and initialize memory for a ByteArray# (Haskell representation).
166 StgArrBytes
* allocateByteArray(Capability
*cap
, StgWord bytes
) {
167 // Idea stolen from PrimOps.cmm:stg_newArrayzh()
168 StgWord words
= sizeofW(StgArrBytes
) + bytes
;
170 StgArrBytes
* array
= (StgArrBytes
*) allocate(cap
, words
);
172 SET_HDR(array
, &stg_ARR_WORDS_info
, CCS_SYSTEM
);
173 array
->bytes
= bytes
;
177 static void copyPtrsToArray(StgArrBytes
* arr
, StgStack
* stack
) {
179 StgStack
*last_stack
= stack
;
180 const StgInfoTable
**result
= (const StgInfoTable
**) arr
->payload
;
182 StgPtr sp
= last_stack
->sp
;
183 StgPtr spBottom
= last_stack
->stack
+ last_stack
->stack_size
;
184 for (; sp
< spBottom
; sp
+= stack_frame_sizeW((StgClosure
*)sp
)) {
185 const StgInfoTable
* infoTable
= ((StgClosure
*)sp
)->header
.info
;
186 result
[index
] = infoTable
;
190 // Ensure that we didn't overflow the result array
191 ASSERT(index
-1 < arr
->bytes
/ sizeof(StgInfoTable
*));
193 // check whether the stack ends in an underflow frame
194 StgUnderflowFrame
*frame
= (StgUnderflowFrame
*) (last_stack
->stack
195 + last_stack
->stack_size
- sizeofW(StgUnderflowFrame
));
196 if (frame
->info
== &stg_stack_underflow_frame_d_info
197 ||frame
->info
== &stg_stack_underflow_frame_v16_info
198 ||frame
->info
== &stg_stack_underflow_frame_v32_info
199 ||frame
->info
== &stg_stack_underflow_frame_v64_info
) {
200 last_stack
= frame
->next_chunk
;