| **ASPAlliance: Articles, reviews, and samples for .NET Developers**  
**URL:**  
http://aspalliance.com/articleViewer.aspx?aId=1341&pId=-1  
---|---  
|  The Infamous Debug=True Attribute in ASP.NET  
  
---  
page  
---  
by Rahul Soni  
  
  
---  
Average Rating:   
Views (Total / Last 10 Days): 93190/ 432  
Introduction  
You probably will not believe it when I simply tell you that <compilation debug="true"> can hurt your application’s performance as bad as it does in ASP.NET production websites! I have already seen a lot of really good articles and blog posts related to this topic telling what can go wrong when you have debug set to true in ASP.NET production websites. In this article I will try to show it in a little more detail so that you are well informed on this attribute and why I am asking you to turn it off in production websites. NOTE: Going forward in this article, wherever I say web.config I mean web.config in your production websites (and not in development machines) until mentioned otherwise explicitly. Also, I am not talking about the global web.config which comes with .NET 2.0. I am just talking about all the web.config files in the root directory of your ASP.NET application’s Virtual Directory/Website. Changing anything in your web.config will raise a File Change Notification event and your application domain will recycle and your application users might get logged off if you are using In Process sessions. So, if you want to make any changes to any of the web.config files, do it in your application's maintenance time. NOTE: Although I am using Microsoft Visual Web Developer 2005 Express Edition for all the samples, I will cover both .NET 1.1 and 2.0 in this article. If you do not want to read the nitty-gritty of this attribute and trust me straight away, find all your web.config files with <compilation debug=“true”> and change it to <complication debug=“false”>. In case you are of the curious types… take a plunge.  
Problem #1  
You will see a lot more files in Temporary ASP.NET files folder with debug=true. Let us do a small exercise (meant only for Development machines and should not be done on a live server). I have created a simple website called DebugTrue in Visual Studio 2005. It has the following structure: Figure 1 – Solution Explorer As you can see, the Solution Explorer contains 5 files (Main.aspx, One.aspx, two.aspx, Global.asax and web.config) at the root level. There is a SomeFolder folder which contains two more files called Three.aspx and Four.aspx. Now navigate to C:\<WINDOWS>\Microsoft.NET\Framework\v2.0.50727\Temporary ASP.NET Files folder and delete everything. If you are unable to delete, close the Visual Studio IDE and try again. If you still have issues, reset IIS. After that, double click on Main.aspx and you will see something as follows. Figure 2 – List of files created automatically in Temporary ASP.NET Files folder  Notice that Global.asax got compiled automatically along with main.aspx. But so far, there are no signs of other ASPX files. Let us double click on Four.aspx in the Solution Explorer of the Visual Studio IDE and you will see something similar to Figure 3.  Figure 3 – Updated List of files after you double-click on four.aspx in Figure 1 Have a look at the difference and check how two more files get created as soon as you opened four.aspx in the IDE. This happens due to the automatic compilation model of Visual Studio 2005. Let us go one step further now; close the IDE and delete everything from the Temporary ASP.NET Files folder. Open the website in Visual Studio 2005 again and click on Build -> Build Web Site. Have a look at the folder and you will see something similar to the following. Figure 4 – List of files when you build the website with debug=true in .NET 2.0 Hawk-eyed folks will have already noticed that .ccu files (Figure 2 & 3) are gone. Instead, you see App_Web_Random.n.vb. You can easily open most of the files in Notepad and check out what they contain. The moral of the exercise done so far is that you get a lot more files when you have debug = true. Open web.config and make debug=false. Remove all your Temporary files and build the website once again. Here is what I got. Figure 5 – List of files when you build the website with debug=false in .NET 2.0 As you can see, there are just 9 objects in Figure 5 as against 34 objects in Figure 4 with debug=true. In .NET Framework 1.1 things were much worse and thus, much bigger performance impact. An application with the same structure but running under Framework 1.1 would show something like the following. Figure 6 – List of files when you build the website with debug=true in .NET 1.1 Notice that now you have 6 assemblies (DLLs); one is for Global.asax and one is for each of the .aspx files! Thus, if you have 200 aspx files in your application there will be 200 DLLs sprayed across the memory. Let us turn debug=false and have a look at the Temporary files folder. Figure 7 – List of files when you build the website with debug=false in .NET 1.1 You have just 3 assemblies now: 1 for Global.asax, 1 for all the files in root folder and 1 for all the files in SomeFolder folder.  
Problem #2   
Your pages will not timeout when you have debug=“true.” It is simply because while debugging you will not really like it that you are in middle of your debugging session and you get an error saying connection timed out or something similar. This attribute allows you to debug your ASP.NET application at your own pace. In the development box this makes sense, but in the production server, long running requests may cause requests to queue and the application to ultimately hang. Let us check this since it is pretty simple. Ensure that debug=“true” in the web.config file and build the website. Now, open one of the files with .vb extension in your Temporary ASP.NET files folder. In the .vb file, you will find a Public method called New(). Listing 1
    
    
    Public Sub New()
       MyBase.New
       Dim dependencies() As String
       CType(Me,System.Web.UI.Page).AppRelativeVirtualPath = "~/One.aspx"
       If (Global.ASP.one_aspx.__initialized = false) Then
         dependencies = New String(0) {}
         dependencies(0) = "~/One.aspx"
         Global.ASP.one_aspx.__fileDependencies = Me.GetWrappedFileDependencies(dependencies)
         Global.ASP.one_aspx.__initialized = true
       End If
       Me.Server.ScriptTimeout = 30000000
     End Sub

As you can see above, this temporary .vb file was created for One.aspx. Also notice that ScriptTimeout is set to 30 million seconds which is approximately 347 days! This is the reason why your pages just will not time out while debugging. If you turn debug=false (Figure 5), the .vb files itself are no longer generated and eventually your pages will start expiring as usual. The timeout of the requests are defined by httpRuntime element in your machine.config (for .NET 1.1). Listing 2
    
    
    <httpRuntime 
       executionTimeout="90"
       maxRequestLength="4096"
       useFullyQualifiedRedirectUrl="false"
       minFreeThreads="8"
       minLocalRequestFreeThreads="4"
       appRequestQueueLimit="100"
       enableVersionHeader="true" 
     />

In.NET 2.0 the httpRuntime element is not explicitly defined either in the Machine.config file or in the root Web.config file. However, the following settings are the default values as initialized by the system. If you need to customize this section, you must create it in your configuration file and define only those attributes that need customization. Listing 3
    
    
    <httpRuntime
       executionTimeout="110"
       maxRequestLength="4096"
       requestLengthDiskThreshold="256"
       useFullyQualifiedRedirectUrl="false"
       minFreeThreads="8"
       minLocalRequestFreeThreads="4"
       appRequestQueueLimit="5000"
       enableKernelOutputCache="true"
       enableVersionHeader="true"
       requireRootedSaveAsPath="true"
       enable="true"
       shutdownTimeout="90"
       delayNotificationTimeout="5"
       waitChangeNotification="0"
       maxWaitChangeNotification="0"
       requestPriority="Normal"
       enableHeaderChecking="true"
       sendCacheControlHeader="true"
       apartmentThreading="false"
    />

For details about all the attributes of httpRuntime, please visit this MSDN article.  
Problem #3  
Batch compilation will be disabled even if the batch attribute is true in the <compilation> element. This attribute is found in your machine.config (in ASP.NET 1.1) in the compilation element as you can see below. By default, the batch attribute is set to true, but this is something which gets overridden by debug=true. This means that even if the batch is set to true, debug=true does not let the compiler do batch compilation!!! Instead, each aspx file ends up with a separate assembly as you saw in Figure 6.  Listing 4
    
    
    <!-- compilation Attributes:
       tempDirectory="directory"
       debug="[true|false]"    // Default: false
       strict="[true|false]"   // Default: false
       explicit="[true|false]" // Default: false
       batch="[true|false]"    // Default: true
       batchTimeout="timeout in seconds" // Default: 15 seconds
       maxBatchSize="max number of pages per batched compilation" 
      // Default: 1000 classes
       maxBatchGeneratedFileSize="max combined size (in KB) of the generated source
       files per batched compilation" Default: 3000KB
       numRecompilesBeforeAppRestart="max number of recompilations before appdomain
       is cycled" Default: 15 recompilations
       defaultLanguage="name of a language as specified in a <compiler/> tag below" 
      // Default: VB
     -->

What does all this mean in plain English?  Well, you might have already noticed that the first time you access any page in your asp.net application, it renders a bit slowly. When you access the same page again it is blazingly fast. Now, if you access another page from the same folder you see that this page will come up fast as well. The delay is due to JIT compilation, but why do you other pages render fast? It happens because the first access to any page in the same folder compiles all the pages of that folder into one assembly. The maxBatchSize attribute is set to 1000 classes by default and this means that if there are 1001 classes, you will see just 2 DLL's being created and the first 1000 classes will go into the first DLL and so on. BUT, this happens ONLY when debug=false. When debug=true the output is something like you saw in Listing 6. Thus, if you have 1001 classes and debug=true, you will see 1001 DLL's sprayed all over your memory. This cannot be good for the worker process health. In fact, this tends to fragment the memory very badly until you start seeing Out of Memory exceptions. If you want to verify how these files get compiled into one assembly, have a look at them in ILDASM.exe.  For this sample Global.asax is compiled into 1 DLL; all the other files in the root folder are compiled into one and all the files in SomeFolder folder in another for a total of 3 assemblies! Figure 8 – ILDasm output showing all the batch compiled pages with Debug=False  
Problem #4  
The System.Diagnostics.DebuggableAttribute gets added to all generated code which causes performance degradation. Basically, when you have debug=true, the DebuggableAttribute gets added to all generated code. Figure 9 – ILDasm output showing all the batch compiled pages with Debug=True  In the figure above, I have just double clicked on Manifest to show that System.Diagnostics.DebuggableAttribute gets added in all the code. The DebuggableAttribute controls how the runtime treats code within the module. The runtime might track extra information about generated code and it might disable certain optimizations based on the values contained within this attribute.   
Problem #5  
Scripts and images downloaded from the WebResources.axd handler (in ASP.NET 2.0 only) are not cached by the browser when debug=true.  We have seen quite a few issues lately where the customers complain that the pages are running too slowly. They look at Fiddler traces/Netmon and figure out that WebResources.axd is the culprit and is getting called multiple times, thus reducing the performance of the webpage. The explanation to this behavior is pretty simple. While developing, you keep debug=true and there are chances that you will be modifying the scripts, validators, treeview images and other client resources handled by webresources.axd. You would really not appreciate it if the script and images downloaded from webresources.axd get cached in the browser’s memory because you will have to clear the browser cache every time to check your application. Thus, what is a perfectly reasonable thing to do in development scenario could turn out to be a very big bottleneck on production websites.  When you set debug=false, the cache duration for the items handled by webresource.axd is set to such a value that it is cached at the client (and intermediate proxy servers) as well. Look at the details below. I have modified one.aspx page of this project (see Listing 1) and it contains the following in the page’s <form> tag. Listing 5
    
    
    <div>
      <asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
      <asp:RangeValidator ID="RangeValidator1" runat="server"
       ErrorMessage="RangeValidator" ControlToValidate="TextBox1" MaximumValue="100"
       MinimumValue="10"></asp:RangeValidator>
      <asp:Button ID="Button1" runat="server" OnClick="Button1_Click" Text="Button" />
     </div>

I am using a simple RangeValidator which is checking a Textbox for values between 10 and 100. This is done to ensure that webresource.axd comes into play. Another thing which you need to do is install Fiddler. This is a free HTTP Debugger tool and I have used this tool to copy the headers of the requests and responses as you can see below. Let us have a look at the header when debug=true. Listing 6
    
    
    GET /DebugTrue/WebResource.axd?d=QmP4A6B4y3bRHKeLCMYC2A2&t=633176900891406250 
    HTTP/1.1
     Accept: */*
     Referer: <a href="http://rahulsoni/debugtrue/one.aspx">http://rahulsoni/debugtrue/one.aspx</a>
     Accept-Language: en-us
     UA-CPU: x86
     Accept-Encoding: gzip, deflate
     User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)
     Proxy-Connection: Keep-Alive
     Host: rahulsoni
     Cookie: ASP.NET_SessionId=nytwqw450gtuax55sqzmz3mh
    HTTP/1.1 200 OK
     Server: Microsoft-IIS/5.1
     Date: Sun, 24 Jun 2007 16:12:25 GMT
     X-Powered-By: ASP.NET
     X-AspNet-Version: 2.0.50727
     Cache-Control: private                             
     Content-Type: application/x-javascript
     Content-Length: 20931

Now, look at the header when debug=false. Listing 7
    
    
    GET /DebugTrue/WebResource.axd?d=QmP4A6B4y3bRHKeLCMYC2A2&t=633176900891406250 
    HTTP/1.1
     Accept: */*
     Referer: <a href="http://rahulsoni/debugtrue/one.aspx">http://rahulsoni/debugtrue/one.aspx</a>
     Accept-Language: en-us
     UA-CPU: x86
     Accept-Encoding: gzip, deflate
     User-Agent: Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)
     Proxy-Connection: Keep-Alive
     Host: rahulsoni
     Cookie: ASP.NET_SessionId=nytwqw450gtuax55sqzmz3mh
    HTTP/1.1 200 OK
     Server: Microsoft-IIS/5.1
     Date: Sun, 24 Jun 2007 16:13:31 GMT
     X-Powered-By: ASP.NET
     X-AspNet-Version: 2.0.50727
     Cache-Control: public
     Expires: Mon, 23 Jun 2008 16:13:31 GMT
     Content-Type: application/x-javascript
     Content-Length: 20931

Notice how Expires header is set to an year ahead and how Cache-Control header is changed to Public when you have debug=false. Figure 10 – Fiddler snapshot with debug=<true/false> showing WebResource.axd Have a look at the image above and see how webresource.axd is getting called multiple times when debug=true (RED Box). The request number 55 and 62 are two different requests from the same browser session and you will see 57, 58, 64, and 65 making calls to webresource.axd with cache type as private (7th Column). I changed the debug=false (output after that is in GREEN box above). Notice how the call to webresource.axd is made in 70 and 72 with cache type as public and the expiring date is set to a year ahead! The subsequent click on the button or new requests to one.aspx does not lead to webresoure.axd requests any more. This is a real big performance gain since we are not talking about just the get requests but each and every postback of the pages which deals with webresouce.axd.   
Problem #6  
There are a lot of other issues which can happen due to debug=true, and although changing this attribute to false might not fix the issue completely, it might be able to bring the actual issue to the surface. I will not give any practical examples for this one, but will simply ask you to trust me on this since I have been debugging a lot of these issues for at least 3 years now. For people who are using ASP.NET 2.0, there is a pretty nifty setting in the machine.config that turns off all debug=true with one click. This means that you do not have to find each and every web.config manually to check that setting. Listing 8
    
    
    <configuration>
       <system.web>
         <deployment retail="true"/>
       </system.web>
     </configuration>  
  
What do you lose with debug="false"  
Now having said all the problems with debug=true, I must say that there is a good side to debug=true as well. So, what do you actually lose by setting debug=false? To show this I have added the following code in Page_Load method of Default.aspx. This code will cause an error message whenever you request the page. Listing 9
    
    
    Protected Sub Page_Load(ByVal sender As Object, _
    ByVal e As System.EventArgs) Handles Me.Load
    Dim x As Integer
      x = 0
      x = x / 0
    End Sub

Check the output if you have debug="false" (first part of the code) and the same page with debug="true" (second half of the code) in Listing 10. Listing 10 – Bad Code causing Overflow Exception
    
    
     [OverflowException: Arithmetic operation resulted in an overflow.]
       _Default.Page_Load(Object sender, EventArgs e) +18
       System.Web.UI.Control.OnLoad(EventArgs e) +99
       System.Web.UI.Control.LoadRecursive() +47
       System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint,
         Boolean includeStagesAfterAsyncPoint) +1061
    
    
     
     
     
     [OverflowException: Arithmetic operation resulted in an overflow.]
       _Default.Page_Load(Object sender, EventArgs e) in I:\My Documents\Visual 
         Studio 2005\WebSites\DebugTrue\Default.aspx.vb:8
       System.Web.UI.Control.OnLoad(EventArgs e) +99
       System.Web.UI.Control.LoadRecursive() +47
       System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint,
         Boolean includeStagesAfterAsyncPoint) +1061

Notice that with debug=“true” you get a more detailed stack output and it even tells you that the problem happened on line number 8 in a page located at I:\My Documents\Visual Studio 2005\Websites\DebugTrue\Default.aspx.vb:8. But, do you honestly think that getting this detailed error message with debug=true outweighs the performance gain with debug=false? If there is a scenario where you would answer YES, I would suggest you go ahead turn debug=“true,” fix your problem and turn debug=“false” back again.   
Summary  
If you are running an ASP.NET website on a Production box, change the compilation element’s debug attribute to false. Please!!!