<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Erik Novales &#187; Development</title>
	<atom:link href="http://www.eriknovales.com/blog/index.php/category/development/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.eriknovales.com/blog</link>
	<description>Game and Software Development, plus other stuff</description>
	<lastBuildDate>Sun, 06 Jun 2010 20:12:10 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.0</generator>
		<item>
		<title>A Gross Overgeneralization</title>
		<link>http://www.eriknovales.com/blog/index.php/2010/05/11/a-gross-overgeneralization/</link>
		<comments>http://www.eriknovales.com/blog/index.php/2010/05/11/a-gross-overgeneralization/#comments</comments>
		<pubDate>Wed, 12 May 2010 05:42:05 +0000</pubDate>
		<dc:creator>Erik Novales</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://www.eriknovales.com/blog/index.php/2010/05/11/a-gross-overgeneralization/</guid>
		<description><![CDATA[From David Chisnall: If you find yourself optimizing your code, then it means that the author of your compiler has failed. This is just very, very untrue. Even if you strike algorithmic optimization from the picture, code optimization is still a very important and useful skill to have no matter what sort of programming you’re [...]]]></description>
			<content:encoded><![CDATA[<p>From <a href="http://etoileos.com/news/archive/2010/04/27/2303/">David Chisnall</a>:</p>
<blockquote><p>If you find yourself optimizing your code, then it means that the author of your compiler has failed.</p>
</blockquote>
<p><font color="#555555">This is just very, very untrue. Even if you strike algorithmic optimization from the picture, code optimization is still a very important and useful skill to have no matter what sort of programming you’re doing. Knowing your target platform, knowing the behavior of your compiler or interpreter, and knowing your data (hat tip again to <a href="http://www.insomniacgames.com/blogcast/blog/mike_acton/1500756">Mike Acton</a> for driving this point home to me) can provide you with ideas on how to transform your code and realize massive performance gains.</font></p>
<p><font color="#555555">If you’re writing performance-critical code, no compiler is going to do everything for you – <a href="http://downloads.gamedev.net/pdf/gpbb/gpbb1.pdf">the best optimizer is still between your ears</a>. The idea that poorly-performing code can be blamed on the compiler is simply naïve and defeatist.</font></p>
]]></content:encoded>
			<wfw:commentRss>http://www.eriknovales.com/blog/index.php/2010/05/11/a-gross-overgeneralization/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>More about debugging.</title>
		<link>http://www.eriknovales.com/blog/index.php/2010/02/14/more-about-debugging/</link>
		<comments>http://www.eriknovales.com/blog/index.php/2010/02/14/more-about-debugging/#comments</comments>
		<pubDate>Sun, 14 Feb 2010 07:15:54 +0000</pubDate>
		<dc:creator>Erik Novales</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://www.eriknovales.com/blog/index.php/2010/02/14/more-about-debugging/</guid>
		<description><![CDATA[One of the blogs that I have on my feed list recently reposted this tidbit: There are only two debugging techniques in the universe: printf. /* */ Since I recently posted my own little list of some debugging techniques, I can’t resist weighing in on this assertion. While their list is a bit flip, changing [...]]]></description>
			<content:encoded><![CDATA[<p>One of the blogs that I have on my feed list <a href="http://hacksoflife.blogspot.com/2010/01/debugging-glsl.html" target="_blank">recently reposted</a> this tidbit:</p>
<blockquote><p>There are only two debugging techniques in the universe: </p>
<ol>
<li>printf. </li>
<li>/* */</li>
</ol>
</blockquote>
<p>Since I recently posted <a href="http://www.eriknovales.com/blog/index.php/2010/01/24/debugging/" target="_blank">my own little list</a> of some debugging techniques, I can’t resist weighing in on this assertion. While their list is a bit flip, changing behavior and running experiments to see the changes is, of course, a core debugging technique. However, the claim that debuggers are just extensions of this is a bit too reductionist for my tastes – it’s like claiming that a car is just a horse that takes longer to get tired, or that the printing press is just a scribe that works faster.</p>
<p>I also feel that “debugging techniques” shouldn’t be limited to “things you do to the program,” either – thought experiments, code change analyses, and the like are all valid in my book as well. My definition includes any form of investigation that gives you more insight into the problem.</p>
<p>Completely separate from the article linked above, I had a glance at <a href="http://en.wikipedia.org/wiki/Debugging" target="_blank">Wikipedia’s article on debugging</a>, and…well, there’s a lot of stuff in there that gets my back up:</p>
<ul>
<li>Citing language choice as having an impact on the debugging process is silly – your choice of language may make it more difficult to write buggy code, but once a bug is in there, I’m hard pressed to think of a reason why language choice would have a substantive impact on the actual debugging process. (Saying that C++ makes debugging easier than C because it has single-line comments is not funny.)     </li>
<li>“Generally, high-level programming languages, such as Java, make debugging easier, because they have features such as exception handling that make real sources of erratic behaviour easier to spot. In lower-level programming languages such as C or assembly, bugs may cause silent problems such as memory corruption, and it is often difficult to see where the initial problem happened.”
<p>1) Exception handling is only as useful as the exceptions that are thrown in the code.      <br />2) The claim that “high-level programming languages” like Java don’t suffer from “memory corruption” is misleading. Any <a href="http://en.wikipedia.org/wiki/Imperative_programming" target="_blank">imperative language</a> with side effects is going to be capable of bugs that <em>look</em> like “memory corruption,” and can effectively be treated the same way with regards to the debugging process. (As an aside, it’s interesting to see how the definition of what qualifies as a “low-level language” have shifted over the years…yikes.)      </li>
<li><a href="http://en.wikipedia.org/wiki/Static_code_analysis" target="_blank">Static code analysis</a> tools are meant to be used to fix code problems <em>before</em> you run into the bugs they cause. Using them as an example of a debugging tool is kind of missing the point of using them entirely. <em>lint</em> isn’t going to help you debug why something is busted – it’s only going to tell you that you’re using an uninitialized variable, and that’s something that shouldn’t have been in your compiled code in the first place.</li>
</ul>
<p>&#160;</p>
<p><strong>Addendum to the earlier debugging post: </strong>One important quality of the printf and commenting techniques that I didn’t mention in my earlier post, though, is that they are techniques that will work even if you can’t actually attach a debugger to the process (or if there <em>is</em> no debugger for the environment in which you’re working). Sometimes this characteristic can really save your bacon – I remember having to change the screen background color register (the poor man’s printf, which doesn’t even need the C standard library) on a certain console in order to debug the DVD boot loading code of a game. Each line of code was prefaced with a call to set the background color to a distinct value, and the color remaining on the screen when the console froze allowed me to determine the location of the crash (and, eventually, the solution).</p>
]]></content:encoded>
			<wfw:commentRss>http://www.eriknovales.com/blog/index.php/2010/02/14/more-about-debugging/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Debugging!</title>
		<link>http://www.eriknovales.com/blog/index.php/2010/01/24/debugging/</link>
		<comments>http://www.eriknovales.com/blog/index.php/2010/01/24/debugging/#comments</comments>
		<pubDate>Sun, 24 Jan 2010 08:40:57 +0000</pubDate>
		<dc:creator>Erik Novales</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://www.eriknovales.com/blog/index.php/2010/01/24/debugging/</guid>
		<description><![CDATA[Here’s another Reddit stub dealing with a topic that is near and dear to my heart: debugging! Unfortunately, the comments on that article seem to focus more on the “fuzzy” aspects of debugging – the “go home and mull it over while watching TV” kind of stuff, rather than more concrete debugging techniques. Whenever I [...]]]></description>
			<content:encoded><![CDATA[<p>Here’s <a href="http://www.reddit.com/r/programming/comments/aq4az/proggit_what_do_you_do_when_code_isnt_working_and/" target="_blank">another Reddit stub</a> dealing with a topic that is near and dear to my heart: debugging! Unfortunately, the comments on that article seem to focus more on the “fuzzy” aspects of debugging – the “go home and mull it over while watching TV” kind of stuff, rather than more concrete debugging techniques.</p>
<p>Whenever I run into a bug whose cause is not immediately obvious, I have a standard bag of tricks that I fall back upon. Every programmer has a toolbox like this – I figured I would write about some of the techniques that I use, and why I use them. Some of them are not applicable to every situation, but there are still many that can be applied to any given bug. These are presented in no particular order.   </p>
<ol>
<li><strong>Change the inputs.</strong>       </p>
<p>Many times, by changing the inputs to a function, you can cause a recognizable change to occur in the output. This helps you to envision what’s actually happening in the function, and where things might be going wrong. This can include changing parameter values, input files, textures, etc.       </li>
<li><strong>Do things in a different sequence, or with different timing.</strong>
<p>Pretty self-explanatory, and related to the first item. The idea is to observe differences in the behavior of the program in similar circumstances. This is mostly useful for interactive programs.       </li>
<li><strong>Run all of your automated test code, even the slow tests, and examine any issues that are reported.</strong>
<p>This is helpful if for no other reason than as a sanity check.       </li>
<li><strong>Check the logs.
<p></strong>This one is pretty standard. Even though most debug logs tend to be overflowing with spam messages, you might still find a smoking gun in there.       </li>
<li><strong>Ensure that you’re validating all of the return values of function calls.</strong>
<p>This is also known as the “be paranoid” rule, or perhaps the “re-check all of your assumptions” rule. It’s easy to forget to check return values, but it’s crucial to do so. Code that silently ignores failure can cause problems or symptoms unrelated to the function call that actually failed.      </p>
<p>A related problem is returning pointers to objects on the stack – this will result in havoc since they will be freed as the function exits.      </li>
<li><strong>Ensure that pointer values get cleared out when the struct or object to which they point is freed.
<p></strong>Using stale pointer values is a surefire way to get in trouble. Clearing them out when the associated object is freed (except in very, very special circumstances) will help keep you sane.      </li>
<li><strong>Check for any masked exceptions in managed code.</strong>
<p><a href="http://www.eriknovales.com/blog/index.php/2010/01/21/first-chance-net-exception-handling/" target="_blank">I wrote about this the other day</a>. Ensure that no unexpected exceptions are being silently masked in your code.      </li>
<li><strong>Check the data.</strong>
<p>Make sure that the data you’re trying to use is actually valid! The <a href="http://en.wikipedia.org/wiki/Garbage_In,_Garbage_Out" target="_blank">GIGO principle</a> is as true as ever. Check for data out of the expected range, <a href="http://en.wikipedia.org/wiki/NaN" target="_blank">QNANs</a> being generated (which are infamous for screwing up subsequent floating-point operations), the ordering of data, legacy data, and correct offsets/sizes of data.      </li>
<li><strong>Put in additional logging or debug visualization code.</strong>
<p>This can help provide additional information, but this strategy can also backfire, as it can significantly change the timing of your code. (Disk, socket, and/or pipe I/O are relatively expensive operations.) Use with caution. If you have general-purpose code for validating the state of the application, sprinkle calls to that code throughout the application – this can be useful in determining when things go off the rails.      </li>
<li><strong>Check the crash dump, if you have one.</strong>
<p>Post-mortem analysis of core dumps is often extremely useful in tracking down bugs that you didn’t personally witness, or for which the steps to reproduce are lengthy or time-consuming.       </li>
<li><strong>Step through the code in the debugger.</strong>
<p>It can sometimes be quite slow if you’re processing large data sets, but it’s often the easiest way to monitor the control flow of a function.       </li>
<li><strong>Inspect the disassembly.</strong>
<p>This sounds hardcore, but being able to do this is invaluable in some cases. It’s useful not only for checking the compiler’s output, but also for cases where you’re examining a minidump of managed code. Unless <a href="http://blogs.msdn.com/rmbyers/archive/2008/10/30/clr-4-0-advancements-in-diagnostics.aspx" target="_blank">you’re working with CLR4 and Visual Studio 2010</a>, opening these minidumps in Windbg results in callstacks that don’t include line numbers. You do have the instruction pointer value, though, so you can actually print out the disassembly with the <a href="http://msdn.microsoft.com/en-us/library/bb190764.aspx" target="_blank">!u SOS command</a>, and compare the disassembly with the original source code of the function to figure out the exact point at which the crash occurred.       </li>
<li><strong>Inspect memory.</strong>
<p>If you have pointer problems, look at the contents of memory in the debugger to try and figure out what’s going on. A frequent problem is an invalid offset, which results in struct member values “shifting” forwards or backwards. It helps to be familiar with memory representations of things like floating-point numbers – knowing a couple of common values (0x3f800000 == 1.0f, etc.) can be very handy.      </li>
<li><strong>Use conditional or memory breakpoints to isolate the bug.</strong>
<p>If you know that a particular object or memory address is related to the bug, you can set up breakpoints to pick out a particular loop iteration or write to a memory location. In cases where you’re interacting with a large body of unknown code, memory breakpoints can be particularly useful for tracking state changes.       </li>
<li><strong>Try a different build configuration (or turn on asserts).</strong>
<p>This is intended to provoke behavior changes, add validation, and otherwise provide additional data points for determining exactly what’s going on. Turning on validation such as array bounds checks and heap checking can help find some tough bugs (albeit at a tremendous cost in execution speed).       </li>
<li><strong>Run on a different platform, or build with a different compiler.
<p></strong>There are often significant differences in timing and other behavior when you run software on a different platform. Endianness and word size also often differ between platforms, which can expose problems or bad assumptions about data in code. Like changing the inputs, careful observation of these differences can help you get an understanding of what’s actually happening. Additionally, if you are using a different compiler, you may see different warnings or code behavior due to optimization.       </li>
<li><strong>Check the version control history for anything suspicious.</strong>
<p>Examining changes to the source code can give you a good idea of how the behavior of the program has changed (even if you don’t know much about the changed code to begin with), and can shed some light on a bug. Checking all of the cases where a function is called to ensure that they take into account any changed behavior is essential.       </li>
<li><strong>If your codebase easily allows you to do so, try running the buggy code synchronously instead of asynchronously, for testing purposes.</strong>
<p>This can help determine if a race condition is what’s causing the bug to occur. (It should be noted that I’m not crazy about just adding random sleeps into an asynchronous function to determine this – it’s too unreliable for my tastes.)      </li>
<li><strong>Check (and re-check) your data dependencies in asynchronous code.</strong>
<p>Don’t fall into the trap of trying to envision multithreaded code by imagining each possible combination of instruction pointer values. Instead, when trying to prove correctness, focus entirely on data dependencies and ensuring that locks are used correctly and respected. (For deadlock bugs, inspect the order in which locks are taken, and check for proper use of back-off and other algorithms for avoiding deadlock.)      </p>
<p>Note that applying these techniques won’t help you write optimal multithreaded code – this requires much broader insight into the particular algorithm in question, and the overall architecture of the code. However, they will help in tracking down correctness issues.      </li>
<li><strong>Turn off chunks of the code, or switch to a different implementation of an interface.</strong>
<p>If you have multiple providers of an interface available, try using a different one, and see how the behavior of the program changes. (Using null/echo interfaces is a common debugging technique.) Additionally, you can try disabling features of your application to see if they are somehow related to the bug.       </li>
<li><strong>Use third-party validation tools and/or debugging information.</strong>
<p>This includes things like the <a href="http://msdn.microsoft.com/en-us/library/ee422516(VS.85).aspx" target="_blank">Direct3D debug runtime</a>, <a href="http://msdn.microsoft.com/en-us/library/bb429476(VS.80).aspx" target="_blank">FxCop</a>, <a href="http://en.wikipedia.org/wiki/Lint_(software)" target="_blank">lint</a>, <a href="http://valgrind.org/" target="_blank">valgrind</a>, the <a href="http://msdn.microsoft.com/en-us/library/974tc9t1%28VS.71%29.aspx" target="_blank">Visual C++ runtime debug heap functions</a>, the <a href="http://www.microsoft.com/downloads/details.aspx?familyid=c4a25ab9-649d-4a1b-b4a7-c9d8b095df18" target="_blank">Application Verifier</a>, and the <a href="http://msdn.microsoft.com/en-us/library/ms792442.aspx" target="_blank">checked build of Windows</a>. The more debugging aids you have active, the more likely that you’ll get a clue upon which you can act.       </li>
<li><strong>If the code is unfamiliar, find out who wrote it, and start asking them basic questions about it.</strong>
<p>This is similar to the “be paranoid” rule, except that by asking all of these basic questions of the author, you’re forcing <em>them</em> to re-check all of their assumptions. It’s not uncommon to have a <em>eureka</em> moment while explaining a bit of code to someone else.       </li>
<li><strong>Try turning off optimizations for a chunk of code.</strong>
<p>My experience has been that people tend to fall back on the “it must be a compiler bug” <strike>excuse</strike> explanation way earlier than they really should. Nevertheless, turning off optimizations for a section of code might help you debug a problem that occurs in optimized builds. (Whether it’s <a href="http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=101881" target="_blank">a genuine optimizer bug</a>, or, say, a misuse of the <a href="http://en.wikipedia.org/wiki/Restrict" target="_blank">C99 <em><strong>restrict</strong></em> type qualifier</a>, is for you to find out. Anyone interested in using the latter, incidentally, should really read <a href="http://cellperformance.beyond3d.com/articles/2006/05/demystifying-the-restrict-keyword.html" target="_blank">this excellent article by Mike Acton</a> on the topic.) Performing a quasi-“binary search” when turning off optimizations can help minimize the time spent searching for the problem code snippet for a genuine optimizer bug.       </li>
<li><strong>Try running on a different machine, or piece of hardware.</strong>
<p>Hardware failure is another bug explanation of which programmers tend to be a little too fond. However, it does happen occasionally, so it’s definitely something worth testing if you run out of other ideas.      </li>
</ol>
<p>That’s pretty much all I can think of at this point. There are a few more tips that spring to my mind, but they are pretty specific to Windows or Visual Studio development, so I won’t recount them here.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.eriknovales.com/blog/index.php/2010/01/24/debugging/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>First Chance .NET Exception Handling</title>
		<link>http://www.eriknovales.com/blog/index.php/2010/01/21/first-chance-net-exception-handling/</link>
		<comments>http://www.eriknovales.com/blog/index.php/2010/01/21/first-chance-net-exception-handling/#comments</comments>
		<pubDate>Thu, 21 Jan 2010 07:03:43 +0000</pubDate>
		<dc:creator>Erik Novales</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://www.eriknovales.com/blog/index.php/2010/01/21/first-chance-net-exception-handling/</guid>
		<description><![CDATA[I saw this article posted on Reddit’s programming feed*, which talks about a Visual Studio debugging technique for getting first crack at exceptions, before any upstream handlers run. The Debug-&#62;Exceptions dialog can be used to set the debugger to break before any exception handlers fire for a particular exception. This is useful not only for [...]]]></description>
			<content:encoded><![CDATA[<p>I saw <a href="http://www.codehappiness.com/post/Debug-Exceptions-window.aspx" target="_blank">this article</a> posted on <a href="http://www.reddit.com/r/programming/" target="_blank">Reddit’s programming feed</a>*, which talks about a Visual Studio debugging technique for getting first crack at exceptions, before any upstream handlers run. The Debug-&gt;Exceptions dialog can be used to set the debugger to break before any exception handlers fire for a particular exception. This is useful not only for debugging code that interfaces with third-party libraries (where it is often unclear why an exception might be thrown), but also your own code. Why?</p>
<p>Imagine that you have some code that runs in an interactive session, but which has a high-level catch block to catch and report errors. This catch block may attempt to continue execution when an error occurs – for example, writing one file in a batch might fail, but the code should continue writing other files. Unfortunately, the exception log produced by this might not provide sufficient information to debug the issue. For example, many of the I/O exception types fail to include information about <em>which</em> file or directory was being modified when the exception was thrown!</p>
<p>In tracking down cases like these, it’s often easier to set the debugger to catch that particular exception at the <a href="http://blogs.msdn.com/davidklinems/archive/2005/07/18/440150.aspx" target="_blank">first chance</a> it gets, and then examine the state of the calling function where the exception was thrown. Having both a call stack and the specific line of code where the exception was thrown often allows you to see the problem immediately, without further investigation.</p>
<p>Another illuminating debugging exercise is to turn on first chance exception handling for <em>all</em> exceptions, and then see where they are thrown. Code that masks exceptions (by using an untyped, empty catch block) is particularly nasty, as it can leave the program in an ill-defined state, but without any feedback to indicate that something failed. A different problem results from the frequent use of exceptions: they have a significant <a href="http://msdn.microsoft.com/en-us/library/ms973839.aspx" target="_blank">negative performance impact</a>. Turning on first chance exception handling makes it trivial to find these cases, if you hadn’t already noticed the debug window spam that frequent exceptions tend to create.</p>
<p>&#160;</p>
<p>&#160;</p>
<p>* There’s a lot of stuff that gets posted there that gets my (figurative) blood pressure up, so we’ll see how long I stick with it. But I figured it would be a good exercise to read it regularly and write about things I see there.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.eriknovales.com/blog/index.php/2010/01/21/first-chance-net-exception-handling/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Using GPPG and GPLEX with Visual Studio</title>
		<link>http://www.eriknovales.com/blog/index.php/2009/06/27/using-gppg-and-gplex-with-visual-studio/</link>
		<comments>http://www.eriknovales.com/blog/index.php/2009/06/27/using-gppg-and-gplex-with-visual-studio/#comments</comments>
		<pubDate>Sat, 27 Jun 2009 19:28:15 +0000</pubDate>
		<dc:creator>Erik Novales</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://www.eriknovales.com/blog/index.php/2009/06/27/using-gppg-and-gplex-with-visual-studio/</guid>
		<description><![CDATA[Here’s a quick note on a problem I ran into awhile back. I was using the GPPG and GPLEX parser tools as part of a Visual Studio project – the input files for these tools generate C# source files which are then compiled into the project. However, I noticed a problem with the recommended project [...]]]></description>
			<content:encoded><![CDATA[<p>Here’s a quick note on a problem I ran into awhile back. I was using the <a href="http://plas.fit.qut.edu.au/projects/LanguageProcessingTools.aspx" target="_blank">GPPG and GPLEX</a> parser tools as part of a Visual Studio project – the input files for these tools generate C# source files which are then compiled into the project. However, I noticed a problem with the recommended project setup (basically, <a href="http://social.msdn.microsoft.com/forums/en-US/vsx/thread/79f4d7b9-a5af-4cab-92c6-e3cb93caef9d" target="_blank">setting up a MSBuild .targets file for GPPG and GPLEX source files</a>, and then including that target file in the project). Changes that I made to the grammar would only take effect the <strong><em>second</em></strong> time I built the project. The <a href="http://msdn.microsoft.com/en-us/library/bb165379(VS.80).aspx" target="_blank">samples</a> and documentation for MPPG and MPLEX (earlier versions of GPPG and GPLEX) are silent on this issue. After inserting some debug code into my build targets, I determined that the files being output by the GPPG and GPLEX target handlers were correct, but the compiler was still using the older versions of the files. It seemed like it was using a cached copy of the old version of the grammar.</p>
<p>As it turns out, there is a bit of chicanery going on inside Visual Studio that results in this behavior. Visual Studio actually <a href="http://blogs.msdn.com/ed_maurer/archive/2008/06/11/a-tale-of-two-compilers.aspx" target="_blank">runs the C# compiler in-process</a>, as an optimization to avoid process start overhead. This in-process compiler gets fouled up, however, when C# source files are generated as part of a build step – it seems to load all of the source files when the build starts, so it winds up using the old version instead of the freshly generated one.</p>
<p>The solution is to add the <strong>UseHostCompilerIfAvailable</strong> property to the .csproj project file, like this:</p>
<blockquote><p><code></code></p>
<pre>&lt;PropertyGroup&gt;
	&lt;UseHostCompilerIfAvailable&gt;False&lt;/UseHostCompilerIfAvailable&gt;
&lt;/PropertyGroup&gt;</pre>
</p>
</blockquote>
<p>This will force Visual Studio to use the out-of-process compiler, which will cause the correct version of the grammar to be built. Building the project will be a little slower, but it’s better than having to build twice!</p>
]]></content:encoded>
			<wfw:commentRss>http://www.eriknovales.com/blog/index.php/2009/06/27/using-gppg-and-gplex-with-visual-studio/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Optimization Tradeoffs</title>
		<link>http://www.eriknovales.com/blog/index.php/2008/12/01/optimization-tradeoffs/</link>
		<comments>http://www.eriknovales.com/blog/index.php/2008/12/01/optimization-tradeoffs/#comments</comments>
		<pubDate>Tue, 02 Dec 2008 02:40:09 +0000</pubDate>
		<dc:creator>Erik Novales</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://www.eriknovales.com/blog/index.php/2008/12/01/optimization-tradeoffs/</guid>
		<description><![CDATA[This recent post by Raymond Chen highlights some interesting memory optimization scenarios, and the wide-ranging impact of size optimizations. A quick summary: Changing a structure definition such that a bunch of Win32 BOOLs use one bit each saves memory in the struct, because BOOL is typedefâ€™d as an INT. Accessing the bitfield data members requires [...]]]></description>
			<content:encoded><![CDATA[<p>This <a href="http://blogs.msdn.com/oldnewthing/archive/2008/11/26/9143050.aspx" target="_blank">recent post by Raymond Chen</a> highlights some interesting memory optimization scenarios, and the wide-ranging impact of size optimizations. A quick summary:</p>
<ul>
<li>Changing a structure definition such that a bunch of Win32 BOOLs use one bit each saves memory in the struct, because BOOL is typedefâ€™d as an INT.</li>
<li>Accessing the bitfield data members requires additional work (a couple of shifts, in the example). This chews up a little time, and some additional memory for the expanded code. The break-even point for overall reduced memory usage depends on the number of struct instances and the number of locations in the code that access the data.</li>
<li>Packing data members in this fashion also prevents you from setting a data breakpoint on a particular packed flag or value.</li>
<li>The atomic read/write operations work on words, making it more cumbersome to manipulate the packed values safely in a multithreaded environment.</li>
</ul>
<p>Changing data members to bitfields has other effects, as well:</p>
<ul>
<li>When making this change, you might do a quick grep on your code (as part of your cost-benefit analysis) to see where the data member is touched. â€œAh ha,â€ you say, â€œitâ€™s only touched in one or two places! This change is a no-brainer!â€ Of course, if those one or two places are part of inlined functions, the impact of the higher instruction count might be much larger than you think. Itâ€™s important to dig a little deeper than a quick grep when making these sorts of changes, to make sure you arenâ€™t psyching yourself out. (And, of course, being able to provide concrete before-and-after measurements is also of paramount importance.)</li>
<li>Depending on your <a href="http://msdn.microsoft.com/en-us/library/ms253935(VS.80).aspx" target="_blank">struct packing size</a>, you might not see any benefit to bitfield optimization unless you reduce the structâ€™s size below the next lower multiple of the packing size.</li>
<li>One potential <em>positive</em> effect is that reducing the size of your structs may help with cache utilization â€“ if you can cram more data into the cache, the added cost of accessing the packed data may be offset.</li>
</ul>
<p>The take-away from this exercise is that itâ€™s important to consider <em>many</em> different factors and effects when optimizing. Most of the real-world cases where I made these optimizations were situations where the struct in question had hundreds, or thousands, of instances, and the tradeoff was definitely a win, but itâ€™s still worth taking a moment to think about all of the consequences of such a change.</p>
<p>The comments on the post are amusing, running the gamut from â€œcompletely missing the pointâ€ (those who say you should <a href="http://blogs.msdn.com/oldnewthing/archive/2008/11/26/9143050.aspx#9146764" target="_blank">always</a>/<a href="http://blogs.msdn.com/oldnewthing/archive/2008/11/26/9143050.aspx#9145821" target="_blank">never</a> use bitfields â€“ there are <a href="http://blogs.msdn.com/oldnewthing/archive/2008/11/26/9143050.aspx#9145266" target="_blank">many</a> of these <a href="http://blogs.msdn.com/oldnewthing/archive/2008/11/26/9143050.aspx#9145333" target="_blank">comments</a>), to those bringing up <em>even more</em> valid considerations (such as how <a href="http://blogs.msdn.com/oldnewthing/archive/2008/11/26/9143050.aspx#9157501" target="_blank">bitfield layout is implementation-dependent behavior</a>, and how <a href="http://blogs.msdn.com/oldnewthing/archive/2008/11/26/9143050.aspx#9152034" target="_blank">trying to map bitfields to hardware structures</a> is a potential endianness minefield). Thereâ€™s also <a href="http://blogs.msdn.com/oldnewthing/archive/2008/11/26/9143050.aspx#9146306" target="_blank">one rather insightful observation</a> that made me laugh:</p>
<blockquote><p>I must say: assembly language has no code of honor. Anything goes so long as it works.</p>
</blockquote>
<p>So true.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.eriknovales.com/blog/index.php/2008/12/01/optimization-tradeoffs/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Braid</title>
		<link>http://www.eriknovales.com/blog/index.php/2008/08/16/braid/</link>
		<comments>http://www.eriknovales.com/blog/index.php/2008/08/16/braid/#comments</comments>
		<pubDate>Sat, 16 Aug 2008 21:25:49 +0000</pubDate>
		<dc:creator>Erik Novales</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Games]]></category>

		<guid isPermaLink="false">http://www.eriknovales.com/blog/index.php/2008/08/16/braid/</guid>
		<description><![CDATA[Braid is a very good game that overcomes some ham-fisted writing and overly ambitious themes. I won&#8217;t really dwell on talking about the game in this post other than to say that I recommend it, and that there are some mild spoilers in the remainder of this post. I saw a post by the author [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://braid-game.com/" target="_blank">Braid</a> is a very good game that overcomes some ham-fisted writing and overly ambitious themes. I won&#8217;t really dwell on talking about the game in this post other than to say that I recommend it, and that there are some mild spoilers in the remainder of this post.</p>
<p>I saw <a href="http://www.quartertothree.com/game-talk/showpost.php?p=1408793&amp;postcount=412" target="_blank">a post by the author</a> commenting on the fact that getting the game to run on the PS3 would be a &quot;gargantuan effort,&quot; and that got me thinking about the reasons why, along with how the game engine works to begin with.</p>
<p>First of all, the time-rewinding mechanism appears to be using <a href="http://www.gamasutra.com/features/19990903/lincroft_02.htm" target="_blank">input recording</a> rather than state recording. That is, the state of the user&#8217;s controller is stored for each frame of the game, and is used to control the player&#8217;s character during time rewinds/fast forwards. I base this idea off of the fact that, in the world that uses your &quot;time shadow,&quot; the shadow&#8217;s actions are based on the player&#8217;s controller input, and the replayed actions can be affected by the current state of the world.</p>
<p>Naturally, there is additional information stored to assist with the time mechanics of the game &#8212; input alone is not enough to reconstruct the state of the world. Jumping, for example, plays a significant role in the game, and being able to &quot;rewind&quot; a jump requires knowledge about when and where the jump started. (If it were possible to efficiently reconstruct the state of the game up to a certain time, you could avoid storing the &quot;where&quot; part, but I don&#8217;t think this is the case.) Additionally, the behavior of AI actors like the bunny and cannons would require some state to be saved in order to properly rewind or replay their actions.</p>
<p>The game runs at a steady 60 FPS. If we assume that replay data is stored at the same frequency, and we assume 128 bytes of replay data per frame (a number that I simply pulled out of a hat that said &quot;reasonable assumptions&quot;), then 15 minutes of gameplay results in about 7 MB of uncompressed replay data. This back-of-the-envelope calculation leads me to believe that the reason that Braid would be difficult to port to the PS3 is <strong>not</strong> related to utilizing more than 256 MB of RAM. (The Xbox 360&#8242;s 512 MB of RAM is unified, whereas the PS3 has a hard separation of 256 MB of main memory and 256 MB of GPU memory.) There should be plenty of memory for the replay data on both platforms, given the relative sparseness of the levels.</p>
<p>With that possibility ruled out, what does that leave? I think it&#8217;s related to the game engine (and replay system) architecture, not the amount of data being created. My guess is that the updates performed by the game engine are, by design, fully reversible, in order to minimize the amount of replay data that needs to be stored. Furthermore, in order to allow for <em>really</em> fast rewinding/replaying (up to 8x, while remaining at 60 FPS), the game update tasks are parallelized.</p>
<p>The game state that is touched by these parallel steps, however, is large enough such that it will not fit into the 256K memory on each SPE of the Cell processor. Furthermore, the game update phase is not organized in any way that would permit &quot;windows&quot; of the game data to be sent to (and modified on) the SPEs. This is pretty much the worst-case performance scenario for the PS3 and the Cell &#8212; the need to parallelize algorithms that touch a large data set in a random-access manner.</p>
<p>I don&#8217;t think this is an insoluble problem, but a solution that would work well on the Cell would likely look drastically different from what you would write if you only had to consider the PC and Xbox 360. And when you consider the size of the programming team that created Braid, it&#8217;s easy to make the choice between rearchitecting the code and moving on to the next game by scrapping the notion of a PS3 port.</p>
<p>Please note that all of the above is just speculation &#8212; I don&#8217;t have any idea if this is actually the case, but I like to speculate about game engine design and implementation, since I&#8217;ve had quite a bit of experience with it&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://www.eriknovales.com/blog/index.php/2008/08/16/braid/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Installers. Again.</title>
		<link>http://www.eriknovales.com/blog/index.php/2008/07/27/installers-again/</link>
		<comments>http://www.eriknovales.com/blog/index.php/2008/07/27/installers-again/#comments</comments>
		<pubDate>Sun, 27 Jul 2008 20:49:51 +0000</pubDate>
		<dc:creator>Erik Novales</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://www.eriknovales.com/blog/index.php/2008/07/27/installers-again/</guid>
		<description><![CDATA[This time, though, my gripe isn&#8217;t with InstallShield, but rather with whoever put together an installer I used recently. As part of my recent hard drive purge, I had to use a partition manager program to reset a drive to NTFS (from extfs) so I could overwrite it with the tools I had at hand. [...]]]></description>
			<content:encoded><![CDATA[<p>This time, though, my gripe isn&#8217;t with <a href="http://www.installshield.com/" target="_blank">InstallShield</a>, but rather with whoever put together an installer I used recently.</p>
<p>As part of <a href="http://www.eriknovales.com/blog/index.php/2008/07/20/getting-rid-of-hard-drives-safely/" target="_blank">my recent hard drive purge</a>, I had to use a partition manager program to reset a drive to NTFS (from extfs) so I could overwrite it with the tools I had at hand. I downloaded and used the demo of&nbsp; <a href="http://7tools.de/" target="_blank">7Tools Partition Manager</a>, which did the trick. (Strangely, &#8220;7Tools&#8221; seems to be a brand/front for <a href="http://www.paragon-software.com/" target="_blank">Paragon Software Group</a>.) However, when I uninstalled the program, it left behind a little surprise which was not to be revealed until a few days later.</p>
<p>I recently started a project to rip my entire audio CD collection (and, at the same time, to disassemble their jewel cases and switch to storing the CDs in binders). I&#8217;ll have more to write about this project later, but for the purposes of this article, the germane bit is that the CD ripping program interfaces with the CD drive through the <a href="http://en.wikipedia.org/wiki/ASPI" target="_blank">ASPI layer</a> (wnaspi32.dll).</p>
<p>On Friday, I went to rip the next batch of my audio CDs, and <a href="http://www.exactaudiocopy.de/" target="_blank">Exact Audio Copy</a> (EAC) began behaving oddly. It would correctly read the track information for CDs (and get the track information from <a href="http://www.freedb.org/" target="_blank">FreeDB</a>), but when I went to actually rip it, it would suddenly claim that there was no CD in the drive. No amount of coaxing or tomfoolery would convince it otherwise. Attempts to use the feature autodetect, read mode autodetect, and C2 check would fail. After going through this cycle a couple of times, and with different CDs, I was convinced that there wasn&#8217;t anything wrong with the hardware itself, nor EAC.</p>
<p>A random Google search for the symptoms I was seeing produced some hits. These were forum posts, usually for people with dodgy or missing copies of the ASPI driver, so I checked my copy of wnaspi32.dll, discovering, to my surprise, that it was left over from 7Tools Partition Manager. (The &#8220;last modified&#8221; date, as well as the presence of a digital signature from Paragon Software Group, were what tipped me off. The digital signatures tab can be viewed in the Properties dialog for the file.) I had to reboot in safe mode to delete it, although I&#8217;m not sure that was strictly necessary &#8212; I may have inadvertently left a program open that was using it.</p>
<p>Naturally, after deleting it, everything was fine. My speculation is that, behind the scenes, one of the two following scenarios took place:</p>
<ul>
<li>The demo version of 7Tools Partition Manager had a &#8220;nag timer&#8221; that would pop up after using the program for a certain period of time, asking you to buy it. The expectation of the programmer was that this screen was to be launched from Partition Manager itself, not from some other program via the ASPI driver, and some sort of problem occurred inside the driver as a result of this false assumption.</li>
<li>Alternately, the demo would simply stop working after a few days, and included code in the drivers to prevent any attempt to circumvent the time restriction. These restrictions were not visible from other programs using the ASPI layer, except as mysterious errors.</li>
</ul>
<p>I haven&#8217;t kept up with InstallShield lately, but I think it would be really helpful if there was some kind of testing feature that allowed a developer to check what&#8217;s left behind after installing a program, running it, and then uninstalling it. (This would also be useful for verifying that user preferences and data which <em>is</em> meant to persist across installations is stored in the correct location.) Making these scenarios easier to test would make it easier for developers to do the right thing, and be conscientious about their &#8220;residue.&#8221; The closer installers get to the ideal (a cross between the <a href="http://en.wikipedia.org/wiki/Hippocratic_Oath" target="_blank">Hippocratic Oath&#8217;s</a> &#8220;do no harm&#8221; and the outdoors philosophy of <a href="http://www.lnt.org/" target="_blank">&#8220;leave no trace&#8221;</a>), the better the user experience will be.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.eriknovales.com/blog/index.php/2008/07/27/installers-again/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>InstallShield Wastes My Valuable Time (Which I Could Be Using To Play a Game)</title>
		<link>http://www.eriknovales.com/blog/index.php/2008/05/17/installshield-wastes-my-valuable-time-which-i-could-be-using-to-play-a-game/</link>
		<comments>http://www.eriknovales.com/blog/index.php/2008/05/17/installshield-wastes-my-valuable-time-which-i-could-be-using-to-play-a-game/#comments</comments>
		<pubDate>Sat, 17 May 2008 17:33:11 +0000</pubDate>
		<dc:creator>Erik Novales</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Games]]></category>

		<guid isPermaLink="false">http://www.eriknovales.com/blog/index.php/2008/05/17/installshield-wastes-my-valuable-time-which-i-could-be-using-to-play-a-game/</guid>
		<description><![CDATA[I&#8217;ve had to use InstallShield to create installers a couple of times. These were fairly unpleasant experiences &#8212; while InstallShield is indeed a pretty flexible tool, it also takes a lot of work to get anything accomplished. Their habit of releasing new versions of the package every year (and completely abandoning the previous versions) makes [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve had to use <a href="http://www.installshield.com/" target="_blank">InstallShield</a> to create installers a couple of times. These were fairly unpleasant experiences &#8212; while InstallShield is indeed a pretty flexible tool, it also takes a lot of work to get anything accomplished. Their habit of releasing new versions of the package every year (and completely abandoning the previous versions) makes yearly sports game updates look benign in comparison, and the pricing is pretty outrageous. The company was purchased by Macrovision, and has been renamed under the execrable banner of <a href="http://www.acresso.com/" target="_blank">Acresso Software</a>. I bet somebody got paid a lot of money to come up with that terrible name &#8212; probably the same people that came up with Accenture or other word-vomit.</p>
<p>(By the way, a quick look at their home page is all you need to reinforce the idea that InstallShield is all about squeezing every last dollar out of you. &quot;Entitlement Relationship Management&quot;? &quot;Enterprise Software Compliance&quot;? A picture of some smiling d-bag in a suit behind two rows of computer monitors? Bleagh. Oh, and their corporate rebranding has resulted in a web site with broken download links &#8212; try downloading updates for InstallShield 5, and have fun!)</p>
<p>As it so happens, yesterday I was trying to install a value edition copy of <a href="http://www.mobygames.com/game/heroes-of-might-and-magic-iv-complete" target="_blank">Heroes of Might and Magic IV Complete</a> (in German, no less). The autorun program on the CD allows you to launch the installers for all three products on the disc. However, when I clicked to launch an installer, nothing would happen. The Task Manager revealed that setup.exe <em>was</em> running, but didn&#8217;t appear to be doing anything &#8212; it was hung up. Attaching a debugger to the process revealed that it was deadlocked &#8212; not good. (I couldn&#8217;t tell what function it was deadlocked in, unfortunately&#8230;I didn&#8217;t have the debugger <a href="http://www.microsoft.com/whdc/devtools/debugging/debugstart.mspx" target="_blank">set up to use the Windows symbol server</a>.) Checking the properties for setup.exe revealed that it was an installer produced by version 5 of InstallShield.</p>
<p>I spent some time searching online for InstallShield problems, and discovered that older InstallShield versions have problems (<a href="http://forums.relicnews.com/archive/index.php/t-36160.html" target="_blank">lots</a>&#160;<a href="http://www.computing.net/answers/windows-xp/failure-to-run-setupexe-some-appl-/164391.html" target="_blank">and</a> <a href="http://www.techsupportforum.com/microsoft-support/windows-xp-support/26681-installshield-setup-wont-run-under-winxp-sp2.html" target="_blank">lots</a>&#160;<a href="http://community.macrovision.com/archive/index.php?t-96525.html" target="_blank">of</a> <a href="http://kb.acresso.com/selfservice/microsites/search.do?cmd=displayKC&amp;docType=kc&amp;externalId=http--communitymacrovisioncom-archive-indexphpt-139045html&amp;sliceId=&amp;docTypeID=DT_MACROVISIONCOMMUNITY_1_1&amp;dialogID=14211288&amp;stateId=0%200%2014213518" target="_blank">problems</a>) when running under Windows XP Service Pack 2. Note that this was divined from <strong>user</strong> postings, not any sort of official acknowledgment from <strike>InstallShield</strike>Acresso. Also, one of those posts states that at least one of those problems is caused by an uninitialized variable in InstallShield &#8212; awesome. Very confidence-inspiring. <a href="http://knowledge.macrovision.com/selfservice/viewContent.do?externalId=Q108322&amp;sliceId=1" target="_blank">InstallShield&#8217;s own updater page</a> even skips over the version that built the installer I was having problems with (InstallShield 5), for some reason &#8212; it&#8217;s like they don&#8217;t even want to acknowledge that it ever existed.</p>
<p>Unfortunately, most of the user threads I linked either didn&#8217;t have any resolution, or suggested things that didn&#8217;t work for me (turning off the NMIndexingService, for example &#8212; it wasn&#8217;t even running on my machine). However, one suggestion that <em>did</em> work for me was to <strong>install the game by booting into safe mode</strong>. Yuck.</p>
<p>Just to make sure that all of the relevant details get picked up by search engines, if you are running an InstallShield installer (setup.exe) under Windows XP service pack 2 or higher, and it hangs or gets stuck, <strong>run the installer after booting into safe mode and it should work</strong>. This definitely applies to InstallShield 5 installers, and possibly later versions. I haven&#8217;t tried uninstalling the game yet, but I imagine that the same remedy will be required for uninstalling it.</p>
<p>Now, after spending two hours figuring this out, I might actually be able to play the game! Yeehaw.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.eriknovales.com/blog/index.php/2008/05/17/installshield-wastes-my-valuable-time-which-i-could-be-using-to-play-a-game/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Bad Release Practices</title>
		<link>http://www.eriknovales.com/blog/index.php/2008/03/13/bad-release-practices/</link>
		<comments>http://www.eriknovales.com/blog/index.php/2008/03/13/bad-release-practices/#comments</comments>
		<pubDate>Thu, 13 Mar 2008 22:44:58 +0000</pubDate>
		<dc:creator>Erik Novales</dc:creator>
				<category><![CDATA[Development]]></category>

		<guid isPermaLink="false">http://www.eriknovales.com/blog/index.php/2008/03/13/bad-release-practices/</guid>
		<description><![CDATA[I recently ran into some issues with installing PHP on an IIS server, and, in the process of tracking down the problems, came to the conclusion that the PHP Group doesn&#8217;t do enough (any?) release testing, and still treats the Windows platform (regardless of whether or not Apache is used) as a second-class citizen. Why? [...]]]></description>
			<content:encoded><![CDATA[<p>I recently ran into some issues with installing <a href="http://www.php.net/" target="_blank">PHP</a> on an IIS server, and, in the process of tracking down the problems, came to the conclusion that the PHP Group doesn&#8217;t do enough (any?) release testing, and still treats the Windows platform (regardless of whether or not Apache is used) as a second-class citizen. Why? Let&#8217;s see:</p>
<ul>
<li><a href="http://bugs.php.net/bug.php?id=41565" target="_blank">The installer will allow you to install modules for which you don&#8217;t have the prerequisites installed.</a> This wouldn&#8217;t be an issue if the PHP interpreter handled this gracefully, but it doesn&#8217;t &#8212; it simply spits out a bunch of dynamic link errors and self-combusts. I agree with the last comment in that bug report &#8212; it may be a case of &#8220;pilot error,&#8221; but the expectation on the Windows platform is that, barring any warnings or errors that are clearly explained to the user, a complete install <strong>should just work</strong>.</li>
<li><a href="http://bugs.php.net/bug.php?id=43913" target="_blank">The current released version of PHP was completely busted on Windows Server 2003</a> &#8212; it doesn&#8217;t read its configuration file correctly. (For the benefit of those who may be Googling for a similar problem, this would result in <a href="http://drupal.org/" target="_blank">Drupal</a>, a CMS I was installing, reporting that &#8220;Your web server does not appear to support any common database types.&#8221;)</li>
<li>Separately from that, <a href="http://bugs.php.net/bug.php?id=43764" target="_blank">the released version of PHP also didn&#8217;t set itself up correctly in IIS as a document handler</a>. For some unfathomable reason, it used 8.3 file names, which have not been necessary in <strong>the last 13 years</strong>. This would cause the document type handler to fail, and 404s to be returned when accessing a PHP file through IIS.</li>
<li>The documentation for the <strong>mysqli</strong> module (as well as the similar-but-different <strong>mysql</strong> module) recommends adding the PHP directory to the Windows PATH variable, for the express purpose of getting the libmysql DLL bundled with the installer to load correctly. This is <em>terrible</em> advice, in my opinion, because, depending on where the user adds the path, the bundled version may take priority over a more recent libmysql installed by MySQL itself. There are additional DLLs in the PHP directory, too, for SSL support, and the same situation could arise there.
<p>While the library vendors may not have a <a href="http://msdn2.microsoft.com/en-us/library/aa376307.aspx" target="_blank">side-by-side versioning</a> scheme for their DLLs, it&#8217;s still no excuse for advocating a dangerous practice.</li>
</ul>
<p>For a product that is <a href="http://www.php.net/usage.php" target="_blank">so widely used</a>, these kinds of problems are really inexcusable, and only contribute to the flaky reputation PHP has.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.eriknovales.com/blog/index.php/2008/03/13/bad-release-practices/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
