Install the trace parser
Set up the server to capture tracing
1. Go to your server configuration in administrative tools.
2. Manage > Create configuration, give it a name, ok
3. Tracing tab:
Check X++ method calls
Check Function calls, Check the 'Sql Statement', 'Bind Variables'
Set number of nested calls to 99
Check Allow client tracing on AOS instance
4. Hit Apply
5. Hit OK
6. Go to your Client configuration in administrative tools.
7. Manage > Create configuration, give it a name, ok
8. Tracing tab:
Check X++ method calls
Check Function calls
Set number of nested calls to 99
9. Hit Apply
10. Hit OK.
11. Restart your AOS.
12. Re-launch your Client configuration screen.
Capture a trace
1. Launch your client as 'Administrator' by righ clicking and perform the action you want to trace once without tracing on (to warm up the caches)
2. In the client config click on "Start tracing"
3. Walk the action you want to trace again.
4. In the Client config click on "Stop tracing".
5. Go to the directory specified at the top of the tracing tab in the server config and sort by modified date. The most recent file should be a .trc file.
Analyze the trace
1. Start the trace parser. If it’s the first time you run it, you’ll be asked a bunch of questions. Be agreeable.
2. File > Import, pick that .trc files you just captured.
3. Start analyzing
I normally first go to the X++/RPC tab and filter by type, totals. Sort by Exclusive RPC calls first, see what’s causing issues. Then sort by inclusive RPC calls and see what’s causing issues. Then Exclusive duration, inclusive duration, etc. General rule of thumb is if you see “your code” in the top couple pages of data for any of those things, you should work to make it better. You can jump to the call stacks causing the calls and stuff. Just look for random ways to make your stuff faster, pull from caches instead of doing round trips, cut down on how many times you’re called, etc. No real science to it, just look for crappy stuff and fix it.
If you want to trace the client it’s the exact same thing, only use the client config instead.
If it doesn’t work
· Make sure you have at least 20 gig of free space on the drive where your log files are located. ETW doesn’t like to run with less than that.
· Make sure you restarted the client or AOS after setting up the tracing options.
· Make sure you wait about 15 seconds after you click “start trace” before you start doing stuff. It takes a couple seconds (usually about 5) before the tracing turns on.
This blog is about daily challenges we normally faced in Dynamics 365 and Dynamics AX development.
Friday, May 21, 2010
Saturday, January 23, 2010
Thursday, August 27, 2009
RunOn property in Axapta
The class has a RunOn property that have three values: Client, Called From and Server. Objects created from the class will then live at the location specified.
If you choose Called from, the object will live at the tier where the code creating it (by calling the new constructor) is running.
Classes extending other classes will also inherit the RunOn property. You cannot change it if it is Client or Server. If it is Called from, you can leave it or change it to Client or Server.
But someone may wonder that menu items have their RunOn properties, what will happen if the RunOn property of a given menu item pointing to a class is set to Server, whereas the class's RunOn property is set to Client. The answer is that only if the Class's RunOn property is set to Called From, the objects will be created determined by menu item's RunOn property.
Also there is another situation, the class has static main method which has a RunOn property as well. What will happen if the menu item's RunOn property is set to client, whereas main method has a server modifier. The answer is it will determined by main method's modifier.
Axapta will create the objects according to the prioritized sequence of Class's RunOn property, Class's main method's modifier, menu item's RunOn property. And please notice that in Fat Client mode, even you can set the Class's RunOn property to Server, Axapta will still create the objects in client side instead of Server side.
If you choose Called from, the object will live at the tier where the code creating it (by calling the new constructor) is running.
Classes extending other classes will also inherit the RunOn property. You cannot change it if it is Client or Server. If it is Called from, you can leave it or change it to Client or Server.
But someone may wonder that menu items have their RunOn properties, what will happen if the RunOn property of a given menu item pointing to a class is set to Server, whereas the class's RunOn property is set to Client. The answer is that only if the Class's RunOn property is set to Called From, the objects will be created determined by menu item's RunOn property.
Also there is another situation, the class has static main method which has a RunOn property as well. What will happen if the menu item's RunOn property is set to client, whereas main method has a server modifier. The answer is it will determined by main method's modifier.
Axapta will create the objects according to the prioritized sequence of Class's RunOn property, Class's main method's modifier, menu item's RunOn property. And please notice that in Fat Client mode, even you can set the Class's RunOn property to Server, Axapta will still create the objects in client side instead of Server side.
Wednesday, August 5, 2009
Create and Post Free Text Invoice in AX
Here is a sample class which is called via Dialog framework to create & post free text invoice using X++ code.
Job:
public void freeTextInvoicePostTestJob()
{
Dialog dialog;
DialogField dlgCustAcc;
DialogGroup dialogPeriodLengthGroup, dialogPeriodLengthGroup1;
DialogField dlgLedgerAcc;
dialog = new Dialog("Free-Text Invoice");
dialogPeriodLengthGroup1 = dialog.addGroup('Cust Table');
dlgCustAcc = dialog.addField(extendedTypeStr(CustAccount));
dialogPeriodLengthGroup = dialog.addGroup('Ledger Table');
dlgLedgerAcc = dialog.addField(extendedTypeStr(LedgerAccount));
if(dialog.run())
{
if(dlgCustAcc.value() && dlgLedgerAcc.value() != '')
FreeTxtInvoiceCreatePost::main(dlgCustAcc.value(), dlgLedgerAcc.value());
else
throw error(strfmt("Either CustAccount or LedgerAccount info is missing."));
}
}
class FreeTxtInvoiceCreatePost
{
}
static void main(CustAccount _custAccount, LedgerAccount _ledgerAccount)
{
CustInvoiceTable custInvoiceTable;
CustInvoiceLine custInvoiceLine;
CustTable custTable;
LedgerTable ledgerTable;
CustPostInvoice custPostInvoice;
LineNum lineNum;
int i;
ttsbegin;
custTable = CustTable::find(_custAccount);
custInvoiceTable.initFromCustTable(custTable);
custInvoiceTable.insert();
ttscommit;
for(i=1; i<=100; i++)
{
ttsbegin;
ledgerTable = LedgerTable::find(_ledgerAccount);
custInvoiceLine.clear();
custInvoiceLine.initValue();
custInvoiceLine.LedgerAccount = ledgerTable.AccountNum;
custInvoiceLine.initFromCustInvoiceTable(custInvoiceTable);
custInvoiceLine.AmountCur = 10.00;
custInvoiceLine.Description = 'FreeTxIv' + int2str(i);
custInvoiceLine.TaxItemGroup = 'full';
custInvoiceLine.ParentRecId = custInvoiceTable.RecId;
lineNum += 1;
custInvoiceLine.LineNum = lineNum;
custInvoiceLine.insert();
ttscommit;
}
custPostInvoice = new CustPostInvoice(custInvoiceTable);
custPostInvoice.run();
}
Job:
public void freeTextInvoicePostTestJob()
{
Dialog dialog;
DialogField dlgCustAcc;
DialogGroup dialogPeriodLengthGroup, dialogPeriodLengthGroup1;
DialogField dlgLedgerAcc;
dialog = new Dialog("Free-Text Invoice");
dialogPeriodLengthGroup1 = dialog.addGroup('Cust Table');
dlgCustAcc = dialog.addField(extendedTypeStr(CustAccount));
dialogPeriodLengthGroup = dialog.addGroup('Ledger Table');
dlgLedgerAcc = dialog.addField(extendedTypeStr(LedgerAccount));
if(dialog.run())
{
if(dlgCustAcc.value() && dlgLedgerAcc.value() != '')
FreeTxtInvoiceCreatePost::main(dlgCustAcc.value(), dlgLedgerAcc.value());
else
throw error(strfmt("Either CustAccount or LedgerAccount info is missing."));
}
}
class FreeTxtInvoiceCreatePost
{
}
static void main(CustAccount _custAccount, LedgerAccount _ledgerAccount)
{
CustInvoiceTable custInvoiceTable;
CustInvoiceLine custInvoiceLine;
CustTable custTable;
LedgerTable ledgerTable;
CustPostInvoice custPostInvoice;
LineNum lineNum;
int i;
ttsbegin;
custTable = CustTable::find(_custAccount);
custInvoiceTable.initFromCustTable(custTable);
custInvoiceTable.insert();
ttscommit;
for(i=1; i<=100; i++)
{
ttsbegin;
ledgerTable = LedgerTable::find(_ledgerAccount);
custInvoiceLine.clear();
custInvoiceLine.initValue();
custInvoiceLine.LedgerAccount = ledgerTable.AccountNum;
custInvoiceLine.initFromCustInvoiceTable(custInvoiceTable);
custInvoiceLine.AmountCur = 10.00;
custInvoiceLine.Description = 'FreeTxIv' + int2str(i);
custInvoiceLine.TaxItemGroup = 'full';
custInvoiceLine.ParentRecId = custInvoiceTable.RecId;
lineNum += 1;
custInvoiceLine.LineNum = lineNum;
custInvoiceLine.insert();
ttscommit;
}
custPostInvoice = new CustPostInvoice(custInvoiceTable);
custPostInvoice.run();
}
Updating records and calling super
Before a record in AX gets updated an external application gets started. This external application is able to do additional update on the same record in AX by using AIF. As the performance of the external route is slow, an additional check is needed to start this process only if needed.
So what do we have?
A. Do I need to compare this with this.orig() before or after super?
B. Do I need to call this external application before or after super?
If we call the external application before super, AX will complain that another user has updated the record. So we have to do it after super. Fine, but what happens…. After super the original record gets equal to the current record. Ouch so the difference between the record and the original record only exists before super. The solution is to make a local buffer variable in the update method that references the original record before calling super. This buffer will still be used after calling super.
Example:
void update()
{
CustTable ctBuffer = this.orig();
Super();
If(this.name != ctBuffer.name)
//call external application
}
So what do we have?
A. Do I need to compare this with this.orig() before or after super?
B. Do I need to call this external application before or after super?
If we call the external application before super, AX will complain that another user has updated the record. So we have to do it after super. Fine, but what happens…. After super the original record gets equal to the current record. Ouch so the difference between the record and the original record only exists before super. The solution is to make a local buffer variable in the update method that references the original record before calling super. This buffer will still be used after calling super.
Example:
void update()
{
CustTable ctBuffer = this.orig();
Super();
If(this.name != ctBuffer.name)
//call external application
}
Tuesday, August 4, 2009
AX 2009 TraceParser
This is very useful link that give some in depths about TraceParser
1. TraceParser Video Training Part I: Installation.
2. TraceParser Video Training Part II: Configuration, Collection and Importing.
1. TraceParser Video Training Part I: Installation.
2. TraceParser Video Training Part II: Configuration, Collection and Importing.
Monday, August 3, 2009
Where do you want your code to run today
Much of the X++ code you write can run on either the Application Object Server, or the Client. The client can be the Win32 client, or BC.NET for Enterprise Portal. MorhpX generally will not direct your code to run on a specific tier, but you as an application developer can control it.
Whether code runs on the server or on the client has performance and security implications, and should be given careful consideration as a part of design. Occasionally it will be correct to methods run on either tier, but usually this is not the case.
As MS move forward implementing tasks in upcoming release, developers will identify which resources to grant access to for a given task. These resources include tables/fields and server entry points. A server entry point is a specially decorated method that runs on the server. Tables/fields accessed inside this server entry point need NOT be identified with the task, greatly reducing the complexity and effort required to implement the task. If the server entry point is NOT executed on the server, then table/field access is still validated, and will fail if the code accesses a table/field that is not included with the task.
So, in short:
Set the RunOn AOT property to Server or Client for new classes, unless you have a reason to leave the default
Include considerations about where the code runs in your design, and look for it when reviewing design documents
Declare static class methods, table static and instance methods with either the "server" or the "client" keyword, unless you have a reason not to
Look for ways to limit direct table and field access in form methods, try to move this into a class
This page MSDN describes where methods run:
http://msdn.microsoft.com/en-us/library/aa891949.aspx. Additional information to cover more object types at http://msdn.microsoft.com/en-us/library/aa634829.aspx.
Whether code runs on the server or on the client has performance and security implications, and should be given careful consideration as a part of design. Occasionally it will be correct to methods run on either tier, but usually this is not the case.
As MS move forward implementing tasks in upcoming release, developers will identify which resources to grant access to for a given task. These resources include tables/fields and server entry points. A server entry point is a specially decorated method that runs on the server. Tables/fields accessed inside this server entry point need NOT be identified with the task, greatly reducing the complexity and effort required to implement the task. If the server entry point is NOT executed on the server, then table/field access is still validated, and will fail if the code accesses a table/field that is not included with the task.
So, in short:
Set the RunOn AOT property to Server or Client for new classes, unless you have a reason to leave the default
Include considerations about where the code runs in your design, and look for it when reviewing design documents
Declare static class methods, table static and instance methods with either the "server" or the "client" keyword, unless you have a reason not to
Look for ways to limit direct table and field access in form methods, try to move this into a class
This page MSDN describes where methods run:
http://msdn.microsoft.com/en-us/library/aa891949.aspx. Additional information to cover more object types at http://msdn.microsoft.com/en-us/library/aa634829.aspx.
Subscribe to:
Posts (Atom)