{"id":3743,"date":"2012-12-10T09:51:35","date_gmt":"2012-12-10T09:51:35","guid":{"rendered":"https:\/\/blogs.msdn.microsoft.com\/visualstudioalm\/2012\/12\/10\/creating-ngen-pdbs-for-profiling-reports\/"},"modified":"2022-07-21T01:56:04","modified_gmt":"2022-07-21T09:56:04","slug":"creating-ngen-pdbs-for-profiling-reports","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/devops\/creating-ngen-pdbs-for-profiling-reports\/","title":{"rendered":"Creating NGEN PDBs for Profiling Reports"},"content":{"rendered":"<p><strong><font color=\"#ff0000\">Update: <\/font><\/strong>Visual Studio 2013 will generate PDBs automatically when using the CPU Sampling or the <a href=\"http:\/\/blogs.msdn.com\/b\/visualstudioalm\/archive\/2014\/02\/28\/new-cpu-usage-tool-in-the-performance-and-diagnostics-hub-in-visual-studio-2013.aspx\">CPU Usage tool<\/a> in the <a href=\"http:\/\/blogs.msdn.com\/b\/visualstudioalm\/archive\/2013\/07\/12\/performance-and-diagnostics-hub-in-visual-studio-2013.aspx\">Performance and Diagnostics hub<\/a>. See <a href=\"http:\/\/blogs.msdn.com\/b\/profiler\/archive\/2014\/07\/09\/automatic-ngen-pdb-generation.aspx\">this post<\/a> for more details!<\/p>\n<p>When profiling managed applications on Windows 8 any samples in an NGEN\u2019d module will appear in the report as [NI module name] instead of showing the function name, which obviously makes it difficult to understand the report.<\/p>\n<p>For example in the screenshot below you see [mscorlib.ni.dll], and I can see in the output window that I failed to load symbols (PDB files) for mscorlib.ni.dll even though I <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/ms241613.aspx#BKMK_Specifying_symbol_locations_and_loading_behavior\">enabled Microsoft Symbol Servers in my Visual Studio symbol settings<\/a> (I can tell that the symbol server is working, because immediately below the \u201cfailed to load\u201d for mscorlib.ni.dll I can see that I did load symbols for twinapi.dll)<\/p>\n<p><img decoding=\"async\" title=\"image\" border=\"0\" alt=\"image\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2012\/12\/4214.image_thumb_1154766D.png\" width=\"722\" height=\"213\" \/><\/p>\n<p>Unfortunately without symbols, you cannot see function names in native modules so all I can tell in the above screenshot is that I\u2019m calling something in mscorlib.ni.dll but I have no idea which function I am calling; where if I\u2019m able to get the symbol file for mscorlib.ni.dll I\u2019ll be able to understand what I\u2019m calling in mscorlib that is expensive. So in this blog post I will show you how to resolve this issue in four steps, but first let\u2019s understand why this is happening.<\/p>\n<h2>Background<\/h2>\n<p><a href=\"http:\/\/blogs.msdn.com\/b\/profiler\/archive\/2012\/12\/10\/the-visual-studio-profiler-on-windows-8.aspx\">On Windows 8 the profiler uses a different underlying technology<\/a> than what it does on previous versions of Windows, which is why the behavior is different on Windows 8. With the new technology, the profiler needs the symbol file (PDB) to know what function is currently executing inside NGEN\u2019d images.<\/p>\n<p>NGEN modules are native modules that are generated on the local machine from the original IL module, so there is no PDB file on the Microsoft symbol servers for framework modules. Furthermore, during NGEN on the local machine there is no PDB file automatically generated for you. Fortunately it\u2019s possible to generate the PDB file yourself and point the profiler to it, so let\u2019s look at the four steps necessary to do that.<\/p>\n<h2>Step 1: Determine where the NI module is on disk<\/h2>\n<ol>\n<li>Go to the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/dd255407.aspx\">\u201cModules\u201d view<\/a> in the profiler report.\u00a0\u00a0\u00a0\u00a0\u00a0<br \/>\n<img decoding=\"async\" title=\"image\" border=\"0\" alt=\"image\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2012\/12\/3632.image_thumb_0315FD7D.png\" width=\"366\" height=\"260\" \/> <\/li>\n<li>Right click on any column header <\/li>\n<li>Choose \u201cAdd\/Remove Columns\u2026\u201d from the context menu\u00a0\u00a0\u00a0\u00a0<br \/>\n<img decoding=\"async\" title=\"image\" border=\"0\" alt=\"image\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2012\/12\/5873.image_thumb_42DFE402.png\" width=\"373\" height=\"265\" \/> <\/li>\n<li>In the dialog box that pops up, choose \u201cModule Path\u201d\u00a0\u00a0\u00a0\u00a0<br \/>\n<img decoding=\"async\" title=\"image\" border=\"0\" alt=\"image\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2012\/12\/0676.image_thumb_1BA59ACD.png\" width=\"386\" height=\"378\" \/> <\/li>\n<li>Click OK <\/li>\n<\/ol>\n<p>The module path column now tells you where on disk the module was during collection.<\/p>\n<p>Here is a screenshot from my local example:<\/p>\n<p><img decoding=\"async\" title=\"image\" border=\"0\" alt=\"image\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2012\/12\/8816.image_thumb_1B3967D8.png\" width=\"718\" height=\"236\" \/><\/p>\n<h2>Step 2: Create the PDB<\/h2>\n<p>To create the PDB, you\u2019re going to use the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/6t9t5wcf(v=vs.110).aspx\">Native Image Generator (ngen.exe)<\/a> that ships with the .NET framework. Since NGEN\u2019d images are native, it\u2019s important you use the copy of ngen.exe that matches the architecture of the application you are profiling (x86\/x64\/ARM). For example if the application is running 64 bit on Windows 8 RTM then you would need to reference the copy of ngen.exe in \u201cC:WindowsMicrosoft.NETFramework64v4.0.30319\u201d<\/p>\n<ol>\n<li><strong>Open a command prompt on the machine where you ran the application during profiling<\/strong> (if you remote profiled a Windows Store App, you have to do this on the machine you were running the app on while you were profiling. It will not work if you do it on the machine you are viewing the report on) <\/li>\n<li>Decide if you just need to see the names of the functions (use this for .NET libraries*), or if need to be able to get back to the original source file including the ability to <a href=\"http:\/\/blogs.msdn.com\/b\/profiler\/archive\/2010\/01\/19\/vs2010-investigating-a-sample-profiling-report-function-details.aspx\">see which lines of source code were executing most frequently in the Function Details view<\/a> (only recommended for modules that are part of your project):<br \/>\n<strong>If you don\u2019t need to get back to source<\/strong> then the process is very simple: In a command prompt type \u201c[Microsoft.NET Path][ngen.exe]<a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/6t9t5wcf(v=vs.110).aspx\">12<\/a> createPDB [NI module including the full path from step 1] [directory to store PDB]\u201d, and the PDB for the NGEN\u2019d module will be placed in the directory you specified to store the PDB<br \/>\nThis is an example of generating an NGEN PDB for 32bit mscorlib.ni.dll<br \/>\n<img decoding=\"async\" title=\"clip_image012\" border=\"0\" alt=\"clip_image012\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2012\/12\/8422.clip_image012_thumb_7840DA8F.jpg\" width=\"668\" height=\"342\" \/> <\/li>\n<\/ol>\n<blockquote>\n<p>**<br \/>\n  If you do need to get back to source **then you will need the PDB for the original pre-NGEN\u2019d module. Once you\u2019ve located that type \u201c<a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/6t9t5wcf(v=vs.110).aspx\">ngen.exe<\/a> createPDB [NI module including the full path from step 1] [directory to store PDB] \/lines [directory containing the original PDB]\u201d<br \/>\n  <em>NOTE: ngen.exe executes within the security context of the module you are generating the NGEN PDB for. So if you are generating an NGEN PDB for a module that is part of a Windows Store App (this does not mean a framework module such as mscorlib.dll referenced by a Store App), then the output path for the NGEN PDB will need to be in a folder the app has access to (placing it next to the NI module is easiest). Similarly, if you are using the \/lines flag, you will need to ensure that that the original PDB is also in a location accessible to the Windows Store app (again next to the NI is easiest). Once the PDB has been generated, you can move this to any location you like.<\/em><\/p>\n<p>Below is an example of generating an NGEN PDB with the \/lines flag for a module in my Windows Store App.<br \/>\n  <img decoding=\"async\" title=\"clip_image014\" border=\"0\" alt=\"clip_image014\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2012\/12\/1663.clip_image014_thumb_5106915A.jpg\" width=\"657\" height=\"393\" \/><\/p>\n<\/blockquote>\n<hr \/>\n<p>\u00a0<\/p>\n<p>**PDBs from the Microsoft Symbol Servers do not support the \/lines flag, so will not work for generating line level information for .NET modules*<\/p>\n<h2>Step 3: Add the PDB to your symbol path<\/h2>\n<p>In the <a href=\"http:\/\/msdn.microsoft.com\/en-us\/library\/b13s79c0.aspx\">Symbols page<\/a>, add the folders containing the NGEN PDBs you created in step 2. This is what that looks like on my machine:<\/p>\n<p><img decoding=\"async\" title=\"image\" border=\"0\" alt=\"image\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2012\/12\/5775.image_thumb_3AE841A0.png\" width=\"676\" height=\"421\" \/><\/p>\n<h2>Step 4: Open the profiler report<\/h2>\n<p>Close the report showing the [NI modules], and open it again; assuming you did steps 1-3 correctly you will now see method names instead of [NI module]. For example, using the PDBs I created in Step 2 and modifying my symbol path per Step 3, when I reopen the report that generated my original screen shot I see the following:<\/p>\n<p><img decoding=\"async\" title=\"image\" border=\"0\" alt=\"image\" src=\"https:\/\/devblogs.microsoft.com\/devops\/wp-content\/uploads\/sites\/6\/2012\/12\/1588.image_thumb_13ADF86B.png\" width=\"718\" height=\"153\" \/><\/p>\n<p><em>NOTE: The reason the \u201cExclusive Samples %\u201d changed from the first screenshot, is the profiler is now able to distinguish between the individual functions inside mscorlib.ni.dll, where in the first screenshot you only see the total percentage for the entire module<\/em><\/p>\n<h2>In Closing<\/h2>\n<p>Once you\u2019ve created an NGEN PDB for a module, all future reports will reuse the PDBs you have already created. However, if the module is updated or replaced (e.g. it is for your Window Store App and you rebuild it, or there is an update to the .NET framework), then you will need to generate a new NGEN PDB to match the new version of the module.<\/p>\n<p>Hope that helps and I&#8217;d love to hear any questions\/feedback that you may have so please leave a comment below.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Update: Visual Studio 2013 will generate PDBs automatically when using the CPU Sampling or the CPU Usage tool in the Performance and Diagnostics hub. See this post for more details! When profiling managed applications on Windows 8 any samples in an NGEN\u2019d module will appear in the report as [NI module name] instead of showing [&hellip;]<\/p>\n","protected":false},"author":68,"featured_media":45953,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1,225],"tags":[],"class_list":["post-3743","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops","category-git"],"acf":[],"blog_post_summary":"<p>Update: Visual Studio 2013 will generate PDBs automatically when using the CPU Sampling or the CPU Usage tool in the Performance and Diagnostics hub. See this post for more details! When profiling managed applications on Windows 8 any samples in an NGEN\u2019d module will appear in the report as [NI module name] instead of showing [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts\/3743","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/users\/68"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/comments?post=3743"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/posts\/3743\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/media\/45953"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/media?parent=3743"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/categories?post=3743"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/devops\/wp-json\/wp\/v2\/tags?post=3743"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}