This post explains how to find the X++ call stack that caused an AOS crash - before reaching this stage you need to first have captured a memory dump, and then set up WinDbg ready to do some analysis, we have posts which explain both of those steps:

Capturing memory dumps:
http://blogs.msdn.com/b/emeadaxsupport/archive/2010/05/12/possibilities-to-create-memory-dumps-from-crashing-processes.aspx

Setting up WinDbg:
http://blogs.msdn.com/b/emeadaxsupport/archive/2011/04/10/setting-up-windbg-and-using-symbols.aspx

Once you have your memory dumps and have set up WinDbg, just open WinDbg, go to file- open a crash dump, and open the *.dmp file you created.

Note: if you fail to load symbols in WinDbg then you won't be able to follow the steps below, instead see the link below for an alternative way to find the X++ stack:
http://blogs.msdn.com/b/emeadaxsupport/archive/2011/04/10/finding-the-ax-user-and-the-x-call-stack-from-a-memory-dump-the-easy-way.aspx

To find the X++ call stack you need to find certain frames in the kernel call stack – they are “Ax32Serv!interpret::evalFunc”, this is the function in the kernel which runs an X++ method, from there you need to check the variables in that function to find out which class/table, which method and also whether it is a class or a table.

First run off the kernel call stack using this command:

kp

You can also add a hex number to the end of this command to run off more frames of the stack, as by default it will only run off 20 or so frames, if you have a longer call stack you might run:

kp100

Or if you have a stack overflow type issue, so the call stack will be hundreds of lines long then run the command below to return the whole stack, you will know when you reach the bottom of the stack as it will start with ntdll!_RtlUserThreadStart (the Windows function used to start a thread):

kpfffff

Look for the Ax32Serv!interpret::evalFunc frames take the location from the line below and then run:

In AX2009 64 bit:

dw 000000002311b150+30

In AX4 32 bit: take the location from the current evalFunc line (not the line below as in AX2009 64 bit) and run:

dw 20abf7f0+14

I have added the “+30” or the “+14” to the end, this is the location of the class or table ID variable, the number will be in hex so you need to convert it to decimal to see the normal number. You can run “?ffe4” in WinDbg to convert a hex number to decimal where ffe4 is the hex number to convert. Next you can run:

In AX2009 64 bit:

db  000000002311b150+38

In AX4 32 bit:

db  20abf7f0+18

The first two digits returned from this function indicate whether it’s a table (02) or a class (04). Next to find the method name run:

In AX2009 64 bit:

dq  000000002311b150+18

In AX4 32 bit:

db 20abf7f0+18

Then take the first location returned and run:

In AX2009 64 bit:

du  00000000`3ba01260

In AX4 32 bit:

du 08dec1cc

This will return the method name. Below is an example in WinDbg with all of these functions used to find the X++ class and method, for one frame, you would need to run this for each instance of Ax32Serv!interpret::evalFunc that you see in the call stack to find the whole X++ stack. This example uses AX2009 64 bit, but the steps are essentially the same for AX4 32bit just using the different commands mentioned above:


0:010> kp100
Child-SP          RetAddr           Call Site
00000000`02848930 000007fe`fe1f9c24 rpcrt4!NdrServerCall2+0x1d
00000000`02848960 000007fe`fe1f9d86 rpcrt4!DispatchToStubInCNoAvrf+0x14
00000000`02848990 000007fe`fe1fc44b rpcrt4!RPC_INTERFACE::DispatchToStubWorker+0x146
00000000`02848ab0 000007fe`fe2a1386 rpcrt4!RPC_INTERFACE::DispatchToStub+0x9b
00000000`02848af0 000007fe`fe2b0c1b rpcrt4!OSF_SCALL::SendReceive+0x156
00000000`02848ba0 000007fe`fe2b0c0d rpcrt4!NdrpClientCall2+0xa38
00000000`02849310 00000000`008d24b9 rpcrt4!NdrClientCall2+0x1d
00000000`02849340 00000000`00614a9c Ax32Serv!ClientEvalFunc+0x139
00000000`02849410 00000000`006157cc Ax32Serv!callClientEvalFunc+0x1dc
00000000`02849500 00000000`006178d2 Ax32Serv!interpret::Srv_evalFunc+0x6dc
00000000`02849740 00000000`00617fe4 Ax32Serv!interpret::xal_eval_func+0xa92
00000000`02849850 00000000`0061ecb5 Ax32Serv!interpret::xal_eval_id+0x94
00000000`02849890 00000000`0061ee38 Ax32Serv!interpret::evalLoop+0x1d5
00000000`02849910 00000000`005facfe Ax32Serv!interpret::eval+0x58
00000000`02849940 00000000`00616843 Ax32Serv!interpret::CQLEvalProc+0x48e
00000000`0284a0e0 00000000`00616c62 Ax32Serv!interpret::doEval+0x4c3
00000000`0284a2b0 00000000`006178a2 Ax32Serv!interpret::evalFunc+0x322
00000000`0284a370 00000000`00617fe4 Ax32Serv!interpret::xal_eval_func+0xa62
00000000`0284a480 00000000`0061ecb5 Ax32Serv!interpret::xal_eval_id+0x94
00000000`0284a4c0 00000000`0061ee38 Ax32Serv!interpret::evalLoop+0x1d5

0:010> dw 00000000`0284a370+20
00000000`0284a390  f006 06eb 0000 0000 a304 0284 0000 0000
00000000`0284a3a0  ff04 ffff ffff ffff 0000 0000 0000 0000
00000000`0284a3b0  0000 0000 0000 0000 c7fd 005c 0000 0000
00000000`0284a3c0  0000 0000 f006 0000 0004 0000 0400 0000
00000000`0284a3d0  d500 0511 0000 0000 0000 0000 0000 0000
00000000`0284a3e0  f0a8 06eb 0000 0000 cffe 009d 0000 0000
00000000`0284a3f0  0000 0000 0000 0000 0000 0000 0000 0000
00000000`0284a400  0000 0000 0000 0000 dc78 06e1 0000 0000

0:010> ?f006
Evaluate expression: 61446 = 00000000`0000f006

0:010> db 00000000`0284a370+28
00000000`0284a398  04 a3 84 02 00 00 00 00-04 ff ff ff ff ff ff ff  ................
00000000`0284a3a8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00000000`0284a3b8  fd c7 5c 00 00 00 00 00-00 00 00 00 06 f0 00 00  ..\.............
00000000`0284a3c8  04 00 00 00 00 04 00 00-00 d5 11 05 00 00 00 00  ................
00000000`0284a3d8  00 00 00 00 00 00 00 00-a8 f0 eb 06 00 00 00 00  ................
00000000`0284a3e8  fe cf 9d 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00000000`0284a3f8  00 00 00 00 00 00 00 00-00 00 00 00 00 00 00 00  ................
00000000`0284a408  78 dc e1 06 00 00 00 00-00 00 00 00 00 00 00 00  x...............

0:010> dq 00000000`0284a370+8
00000000`0284a378  00000000`0522a216 00000000`06ebec50
00000000`0284a388  00000000`006c0c00 00000000`06ebf006
00000000`0284a398  00000000`0284a304 ffffffff`ffffff04
00000000`0284a3a8  00000000`00000000 00000000`00000000
00000000`0284a3b8  00000000`005cc7fd 0000f006`00000000
00000000`0284a3c8  00000400`00000004 00000000`0511d500
00000000`0284a3d8  00000000`00000000 00000000`06ebf0a8
00000000`0284a3e8  00000000`009dcffe 00000000`00000000

0:010> du 00000000`0522a216
00000000`0522a216  "info"

Next you might want to find out which AX user caused the crash, we have a post on that below:
http://blogs.msdn.com/b/emeadaxsupport/archive/2011/04/10/finding-the-ax-user-that-caused-a-crash.aspx

There is an easier way to do this for AX2009 x64 - we have created scripts to automate the process in WinDbg - see this post:
http://blogs.msdn.com/b/emeadaxsupport/archive/2011/04/10/finding-the-ax-user-and-the-x-call-stack-from-a-memory-dump-the-easy-way.aspx

--author: Tariq Bell
--editor: Tariq Bell
--date: 10/04/2011