Finding the X++ stack and AX user with public symbols in AX2012


This is an extension of a series of articles I wrote some time ago for AX2009 - this just brings it up to date to do it with AX2012. This enables you to take a snapshot of your AOS and see what's running on it - call stacks and user sessions. If you're not familiar with analysing memory dumps then check this post first, it explains how to get set up etc..

http://blogs.msdn.com/b/emeadaxsupport/archive/2011/04/10/so-your-aos-crashed-is-hanging-or-you-just-want-to-see-what-it-s-doing.aspx

Here’s a run through finding the X++ call stack and the AX user for AX2012. When you open the dump in WinDbg, run the command “kv” to see the call stack, you’ll see output like this:

 0:007> kv
Child-SP          RetAddr           : Args to Child                                                           : Call Site
00000000`040b4c40 00000001`406ffc75 : ffffffff`fffffffe 00000000`00000001 00000000`00000001 00000001`403bc721 : Ax32Serv!cqlCursor::connection+0x14
00000000`040b4c70 00000001`406ffec6 : 00000000`1e84a7a0 00000000`1e56c720 00000000`00000001 00000000`040b50b0 : Ax32Serv!cqlCursor::DropTempDBTableInstance+0x75
00000000`040b4eb0 00000001`403b9771 : 00000000`00000001 00000000`00000001 00000000`040b50b0 00000001`40437346 : Ax32Serv!cqlCursor::Dispose+0x26
00000000`040b4ee0 00000001`403bc714 : 00000000`1e56c720 00000001`404351f0 00000000`1ac20d28 00000001`407d8d4b : Ax32Serv!cqlCursor::~cqlCursor+0x101
00000000`040b4f60 00000001`40369815 : 00000000`1e56c720 00000000`194c0d00 00000000`1b912800 00000001`407d8ead : Ax32Serv!cqlCursor::`vector deleting destructor'+0x14
00000000`040b4f90 00000001`403801d5 : 00000000`1e56c720 00000000`040b6100 00000000`1e0bfe40 00000001`4058067b : Ax32Serv!cqlCursor::freeRef_AdHoc+0x35
00000000`040b4fc0 00000001`40582026 : 00000000`040b50b0 00000000`00000006 00000000`040b51a0 00000001`4049e2b1 : Ax32Serv!assignCursor+0x75
00000000`040b4ff0 00000001`40582d95 : 00000000`000000c8 00000000`00000000 00000000`040b51b0 00000000`040b51a0 : Ax32Serv!CQLFreeVars+0x116
00000000`040b5040 00000001`40430c43 : 00000000`1b912800 00000000`00000005 00000000`00000005 00000000`00000005 : Ax32Serv!interpret::CQLEvalProc+0x715
00000000`040b52c0 00000001`4043370a : 00000000`03720cc8 000007fe`8f8fc3a1 00000000`18ebfb90 00000000`1b913940 : Ax32Serv!interpret::doEval+0x3e3
00000000`040b55c0 00000001`40434517 : 00000000`18ebfb00 00000000`1e522736 00000000`1e665e60 00000000`00930200 : Ax32Serv!interpret::evalFunc+0x2ca
00000000`040b56a0 00000001`404351f0 : ffffffff`fffffffe 00000001`4065167a ffffffff`fffffffe 00000000`040b6190 : Ax32Serv!interpret::xal_eval_func+0xc77
00000000`040b6030 00000001`4049e127 : 00000000`1b912800 00000000`1b912800 00000000`040b6190 00000000`040b7000 : Ax32Serv!interpret::xal_eval_id+0xd0
00000000`040b6070 00000001`4049e268 : 00000000`1b912800 00000000`00000000 00000000`1b912800 00000000`040b70e0 : Ax32Serv!interpret::evalLoop+0x167
00000000`040b60d0 00000001`40582b53 : 00000000`00000001 00000000`00000000 00000000`040b6180 00000000`00000000 : Ax32Serv!interpret::eval+0x58

For the X++ stack we care about the ax32serv!interpret::evalfunc frames. Let’s take the evalfunc line nearest to the top of the stack as an example:

00000000`040b55c0 00000001`40434517 : 00000000`18ebfb00 00000000`1e522736 00000000`1e665e60 00000000`00930200 : Ax32Serv!interpret::evalFunc+0x2ca

Take the third column shown above in green and then run “du <the location>” as shown below. This gives you the X++ method name:

0:007> du 00000000`1e522736
00000000`1e522736  "saveBudgetCheckResultErrorWarnin"
00000000`1e522776  "gDetails"

Now to find the class, take the first column in that line and run “dd <the location>” as shown below, this gives you the class ID in hexadecimal:

0:007> dd 00000000`040b55c0+44
00000000`040b5604  000f554e 1b913940 00000000 00000000
00000000`040b5614  00000000 00000000 00000000 00000000
00000000`040b5624  00000000 00000000 00000000 00000000
00000000`040b5634  00000000 00000000 00000000 00000000
00000000`040b5644  00000000 ffffff00 00000000 1e665e60
00000000`040b5654  00000000 fffffffe ffffffff 00000001
00000000`040b5664  00000000 1b913940 00000000 1e522736
00000000`040b5674  00000000 1b912800 00000000 040b7000

To find out what the class name is from this, first you can convert the ID to decimal, just run “? <the ID>”

0:007> ? 000f554e
Evaluate expression: 1004878 = 00000000`000f554e

Above 1004878 is the classID, you can find the class name in an X++ job, like this:

info(classid2name(1004878));

Now for you to find the AX user you run the command “!tls -1”, this is following the instructions from this post but I am giving you different offsets here:

0:007> !tls -1
TLS slots on thread: 2ed4.176c
0x0000 : 0000000000000000
0x0001 : 0000000000000000
0x0002 : 0000000000000000
0x0003 : 0000000000000000
0x0004 : 0000000000000000
0x0005 : 0000000000000000
0x0006 : 0000000000000000
0x0007 : 0000000000e29900
0x0008 : 0000000000000000
0x0009 : 0000000000000000
0x000a : 0000000000000000
0x000b : 0000000000000000
0x000c : 0000000000000000
0x000d : 0000000000000000
0x000e : 0000000000000000
0x000f : 0000000000000000
0x0010 : 0000000000000000
0x0011 : 0000000000000000
0x0012 : 0000000000000000
0x0013 : 0000000000000000
0x0014 : 0000000000000000
0x0015 : 0000000000000000
0x0016 : 0000000000000000
0x0017 : 00000000005c70f0
0x0018 : 0000000000000000
0x0019 : 0000000000000000
0x001a : 0000000000000000
0x001b : 0000000000000000
0x001c : 0000000000000000
0x001d : 0000000000000000
0x001e : 000000000f2442a0
0x001f : 00000000005daca0
0x0020 : 000000000f177e60
0x0021 : 0000000001829210
0x0022 : 000000001916a600
0x0023 : 0000000000000000
0x0024 : 0000000000000000
0x0025 : 0000000000000000
0x0026 : 0000000000000000
…<cut short for display>…

Then take the location above and run the command below, this is to find the user’s session block (the class instance that represents their session). Note that in this example I’ve picked the 0x0022 entry in the list that came from the !tls -1, this won’t always be the same entry – unfortunately with public symbols (i.e. outside of Microsoft) you can’t do the !tls part more accurately, you’ll need to try each entry and has a value other than zero and you’ll know if it’s right in the next step if you see a username at 17c.

0:007> dq 000000001916a600+68
00000000`1916a668  00000000`194c0d00 00000000`0f26f790
00000000`1916a678  00000000`00000000 00000000`00000000
00000000`1916a688  00001d5b`00000000 00000001`3feebdc0
00000000`1916a698  00000000`00000001 00000000`00000000
00000000`1916a6a8  74007300`69004c00 65006700`61005000
00000000`1916a6b8  fff30002`00000000 00073ea8`0e010000
00000000`1916a6c8  02000000`00010001 00000000`000001ff
00000000`1916a6d8  10a8effc`a833b3d8 00000000`19152760

Then take the location in green above, and at various offsets you’ll find information about the user as shown below.

User’s AX username:
0:007> du 00000000`194c0d00+17c
00000000`194c0e7c  "kkidder"

User’s AX company:
0:007> du 00000000`194c0d00+298
00000000`194c0f98  "dat"

User’s client machine name:
0:007> du 00000000`194c0d00+57c
00000000`194c127c  "eeax2008"

Happy debugging!

/Tariq Bell

Comments (4)

  1. Eduard Levanic says:

    Hi Tariq, can you provide the WinDbgScripts for the AX2012 please?

  2. Tommy Skaue says:

    Yes, some scripts would be awesome. 🙂

  3. Nancy says:

    when I run command du <location> it returns wired characters that's unreadable. Is there any configuration or setup missing?

  4. Nancy says:

    0:021> du 00000000`22b7dd00

    00000000`22b7dd00  "盀졜."

    0:021> du 00000004`c85c95a0

    00000004`c85c95a0  "鋠졜."

    0:021> du 00000000`62248bd4

    00000000`62248bd4  "삅.迨.䣿.ᗿ瓊.좋..觿䠃쒃嬠쳃쳌쳌쳌區荈䃬譈.䠏쮋觨ﭦ藿瓀䠓"

    00000000`62248c14  "쮋..䣿삅.荈䃄썛…롁."

    0:021> du 00000000`22b7e3a0

    00000000`22b7e3a0  ""

    0:021> du 000007f7`eb53e000

    000007f7`eb53e000  ""

    0:021> du 00000005`d7034010

    00000005`d7034010  "ꄘ.߷"

    0:021> du 00000004`eeadeda0

    00000004`eeadeda0  "㡨.߷"

Skip to main content