Plugin placed in the Game's Plugin folder are always compiled in optimized mode, even when the Debug or DebugGame configuration is selected

This is an update to this question, which I never got an acceptable response. This is a major issue for me because when the plugin is compiled in optimized mode, it makes debugging it very difficult as single-step and breakpoints don’t work as expected.

  1. Create a new game based any c++ template. Set the configuration to Debug Editor.
  2. Copy the BlankPlugin from the Plugin/Developers folder to the Game/Plugins folder. You may need to rename it and be sure to delete any old binaries.
  3. In the Startup method of the plugin module class, add the line auto x = 3; This is a throwaway line that will be there in the non-optimized code, but optimized away by the compiler.
  4. Add the same line, auto x=3; to a constructor in the game module. This is our reference.
  5. Compile.
  6. Set breakpoints in both the Startup method where you added the line in step 3, and the constructor in line 4
  7. Use F5 to start the debugger.
  8. The first breakpoint hit should be the module and the second should be the constructor. Do a dissembly of both, you should see the same assembly code moving the constant 3 to a memory or register.

When I do the above, in step 8, the code is different in the game module vs the plugin module. The game module shows assembly code for the statement, whereas in the plugin it is optimized away.

Sorry you didn’t get a follow-up on your previous question.

It does optimize the plugin in DebugGame configurations, which is a regression introduced around the 4.3 timeframe (changelist 2081573). I can fix that for the next release and give you a patch if you need one.

I’m seeing the correct behavior with the Debug configuration though, and have checked the command line passed to the compiler in UnrealBuildTool. Is there anything else unusual about your setup?

Ben,

I would like the patch.

Your right about Debug, I just tested it again and verified that it is not optimized, which is correct. I was working from the binary download and not the engine source when I reported and I thought I remembered both having the issue.

Also, is there someway of logging the compile and link commands issued in UBT? I didn’t see one and am wondering if I missed something. I want something like the -n switch for make. If not, there needs to be.

I’ve uploaded a QFE here which will install straight to your 4.4 installation directory.

You used to be able to pass the -verbose option to UBT to get a list of commands that it was executing, but a recent optimization will prevent it from working unless you recompile it with the TRACE define. I’ll try to fix this for the next release.

Thanks, I’ll give it a try.

Recompiling it isn’t a big issue. It’d be nice to have it something I could set in my build.cs file. Changing one line in that file is a lot easier then have to dig on the command line arguments and setting startups.

Ben,

It appears that this change did not make it into 4.5 as I’m still seeing optimized compiles for the game plugin. I’m going to try the patch again to see it fixes the problem. Can you confirm whether or not the fix made it into 4.5?

Yes, the fix should be in 4.5. I’ve just tried running your original repro steps again, and everything seems to work fine. I added some code which is trivially optimized away, and could step through it fine. Is it possible you’re seeing some other issue?

I pulled down the 4.5 release source from GitHub (just checked the version to make sure) and I’m seeing it there. I have a function that calls FPaths::GetPluginsFolder() (?) and returns and it’s getting inlined. If I step through in disassembly mode, I can see it calling the FPaths function, then deleting a TArray. A breakpoint in my function is never hit.

Edit: I also tried setting the OptimizeCode to Never in Build.cs for the plugin and that’s ignored too.

Is GetPluginsFolder() something that you’ve added to Paths.cpp in the engine? I don’t see it in my local codebase. If that’s part of the engine, then it would be optimized in a DebugGame configuration.

Sorry, here’s the correct code:

void LoadCoffeeScript() {
	auto gamePluginPath = FPaths::GamePluginsDir();
	auto x = 3;
}

The auto x=3; statement is just for setting the breakpoint after the call.

Can you make a project which demonstrates the problem and upload it to Dropbox (or somewhere else), perhaps? When I copy that code into the StartupModule() of a plugin compiled in DebugGame Editor, the disassembly looks as I would expect:

class FNewBlankPlugin : public IModuleInterface
{
public:
	virtual void StartupModule() override
	{
000007FED3C918EC  mov         qword ptr [rsp+8],rcx  
000007FED3C918F1  push        rdi  
000007FED3C918F2  sub         rsp,50h  
000007FED3C918F6  mov         rdi,rsp  
000007FED3C918F9  mov         ecx,14h  
000007FED3C918FE  mov         eax,0CCCCCCCCh  
000007FED3C91903  rep stos    dword ptr [rdi]  
000007FED3C91905  mov         rcx,qword ptr [this]  
		 auto gamePluginPath = FPaths::GamePluginsDir();
000007FED3C9190A  lea         rcx,[gamePluginPath]  
		 auto gamePluginPath = FPaths::GamePluginsDir();
000007FED3C9190F  call        qword ptr [__imp_FPaths::GamePluginsDir (07FED3C94160h)]  
		 auto x = 3;
000007FED3C91915  mov         dword ptr [x],3  
	}

I added -verbose and got the log of the compile. I posted one of the compile commands below. I think the problem may be it’s still adding /Ob2 which tells msvc to inline any functions it wants. It also has /Od which disables all optimizations, so I’m not sure what’s going on. If you need the full log or still want me to post a sample project, let me know. But it appears to be dependent on the behavior of the /Ob2 option so I’m not sure I’ll be able to make a new project that displays the behavior.

Edit: I’ve modified UBT to removed the /Ob2 for Debug compiles to see if it fixes the problem. I’ll post an update when the compile finishes.

 Compiling: Module.Coupling.1_of_8.cpp
   Command:  /nologo /Oi /Zp8 /Gy /fp:fast /c /Zm800 /bigobj /wd4819 /wd4651 /wd4005 /Od /Os /Ob2 /RTCs /errorReport:prompt /Z7 /MD /I "D:\Unreal\UnrealEngine\Engine\Source" /I "D:\Unreal\Projects\Research\CouplingTest\Plugins\Coupling\Source" /I "D:\Unreal\Projects\Research\CouplingTest\Plugins\Coupling\Source\Coupling/Private" /I "D:\Unreal\Projects\Research\CouplingTest\Plugins\Coupling\ThirdParty\v8\include" /I "../../../Projects/Research/CouplingTest/Intermediate/Build/Win64/CouplingTest/Inc/Coupling" /I "../../../Projects/Research/CouplingTest/Plugins/Coupling/Source/Coupling/Public" /I "Runtime/Core/Public" /I "Runtime/Core/Public/Internationalization" /I "Runtime/Core/Public/Async" /I "Runtime/Core/Public/Containers" /I "Runtime/Core/Public/Delegates" /I "Runtime/Core/Public/GenericPlatform" /I "Runtime/Core/Public/HAL" /I "Runtime/Core/Public/Logging" /I "Runtime/Core/Public/Math" /I "Runtime/Core/Public/Misc" /I "Runtime/Core/Public/Modules" /I "Runtime/Core/Public/Modules/Boilerplate" /I "Runtime/Core/Public/ProfilingDebugging" /I "Runtime/Core/Public/Serialization" /I "Runtime/Core/Public/Serialization/Json" /I "Runtime/Core/Public/Serialization/Csv" /I "Runtime/Core/Public/Stats" /I "Runtime/Core/Public/Templates" /I "Runtime/Core/Public/UObject" /I "Runtime/Core/Public/Windows" /I "Runtime/Core" /I "Runtime/CoreUObject/Classes" /I "../../../Projects/Research/CouplingTest/Intermediate/Build/Win64/CouplingTest/Inc/CoreUObject" /I "Runtime/CoreUObject/Public" /I "Runtime/CoreUObject/Public/Blueprint" /I "Runtime/CoreUObject/Public/Misc" /I "Runtime/CoreUObject/Public/Serialization" /I "Runtime/CoreUObject/Public/Templates" /I "Runtime/CoreUObject/Public/UObject" /I "Runtime/CoreUObject" /I "Runtime/Engine/Classes" /I "../../../Projects/Research/CouplingTest/Intermediate/Build/Win64/CouplingTest/Inc/Engine" /I "Runtime/Engine/Public" /I "Runtime/Engine/Public/AI" /I "Runtime/Engine/Public/EdGraph" /I "Runtime/Engine/Public/Features" /I "Runtime/Engine/Public/Landscape" /I "Runtime/Engine/Public/Net" /I "Runtime/Engine/Public/Slate" /I "Runtime/Engine/Public/Tests" /I "Runtime/Engine/Public/AI/Navigation" /I "../../../Projects/Research/CouplingTest/Intermediate/Build/Win64/CouplingTest/Inc/Slate" /I "Runtime/Slate/Public" /I "Runtime/Slate/Public/Framework" /I "Runtime/Slate/Public/Widgets" /I "Runtime/Slate/Public/Framework/Application" /I "Runtime/Slate/Public/Framework/Commands" /I "Runtime/Slate/Public/Framework/Docking" /I "Runtime/Slate/Public/Framework/Layout" /I "Runtime/Slate/Public/Framework/MultiBox" /I "Runtime/Slate/Public/Framework/Notifications" /I "Runtime/Slate/Public/Framework/Styling" /I "Runtime/Slate/Public/Framework/Text" /I "Runtime/Slate/Public/Framework/Views" /I "Runtime/Slate/Public/Framework/Text/Android" /I "Runtime/Slate/Public/Framework/Text/IOS" /I "Runtime/Slate/Public/Widgets/Colors" /I "Runtime/Slate/Public/Widgets/Docking" /I "Runtime/Slate/Public/Widgets/Images" /I "Runtime/Slate/Public/Widgets/Input" /I "Runtime/Slate/Public/Widgets/Layout" /I "Runtime/Slate/Public/Widgets/Navigation" /I "Runtime/Slate/Public/Widgets/Notifications" /I "Runtime/Slate/Public/Widgets/Text" /I "Runtime/Slate/Public/Widgets/Views" /I "Runtime/InputCore/Classes" /I "../../../Projects/Research/CouplingTest/Intermediate/Build/Win64/CouplingTest/Inc/InputCore" /I "Runtime/InputCore/Public" /I "Runtime/InputCore" /I "../../../Projects/Research/CouplingTest/Intermediate/Build/Win64/CouplingTest/Inc/SlateCore" /I "Runtime/SlateCore/Public" /I "Runtime/SlateCore/Public/Animation" /I "Runtime/SlateCore/Public/Application" /I "Runtime/SlateCore/Public/Brushes" /I "Runtime/SlateCore/Public/Fonts" /I "Runtime/SlateCore/Public/Input" /I "Runtime/SlateCore/Public/Layout" /I "Runtime/SlateCore/Public/Logging" /I "Runtime/SlateCore/Public/Rendering" /I "Runtime/SlateCore/Public/Sound" /I "Runtime/SlateCore/Public/Styling" /I "Runtime/SlateCore/Public/Textures" /I "Runtime/SlateCore/Public/Types" /I "Runtime/SlateCore/Public/Widgets" /I "Runtime/SlateCore" /I "Runtime/Slate" /I "Runtime/Messaging/Public" /I "Runtime/Messaging/Public/Common" /I "Runtime/Messaging/Public/Interfaces" /I "Runtime/Messaging" /I "Runtime/RenderCore/Public" /I "Runtime/RenderCore" /I "Runtime/RHI/Public" /I "Runtime/RHI" /I "Runtime/ShaderCore/Public" /I "Runtime/ShaderCore" /I "Runtime/AssetRegistry/Public" /I "Runtime/AssetRegistry" /I "Runtime/EngineMessages/Classes" /I "../../../Projects/Research/CouplingTest/Intermediate/Build/Win64/CouplingTest/Inc/EngineMessages" /I "Runtime/EngineMessages/Public" /I "Runtime/EngineMessages" /I "Runtime/EngineSettings/Classes" /I "../../../Projects/Research/CouplingTest/Intermediate/Build/Win64/CouplingTest/Inc/EngineSettings" /I "Runtime/EngineSettings/Public" /I "Runtime/EngineSettings" /I "Developer/SynthBenchmark/Public" /I "Runtime/Renderer/Public" /I "Runtime/Renderer" /I "Developer/SynthBenchmark" /I "Runtime/AIModule/Public" /I "Runtime/AIModule/Classes" /I "../../../Projects/Research/CouplingTest/Intermediate/Build/Win64/CouplingTest/Inc/AIModule" /I "Runtime/AIModule" /I "Runtime/Engine" /I "Runtime/InputDevice/Public" /I "Runtime/InputDevice" /I "Runtime/Navmesh/Public" /I "Runtime/Navmesh/Public/DebugUtils" /I "Runtime/Navmesh/Public/Detour" /I "Runtime/Navmesh/Public/DetourCrowd" /I "Runtime/Navmesh/Public/DetourTileCache" /I "Runtime/Navmesh/Public/Recast" /I "Runtime/Navmesh" /I "Runtime/Networking/Public" /I "Runtime/Networking/Public/Common" /I "Runtime/Networking/Public/Interfaces" /I "Runtime/Networking/Public/Interfaces/IPv4" /I "Runtime/Networking/Public/Interfaces/Steam" /I "Runtime/Sockets/Public" /I "Runtime/Sockets" /I "Runtime/Networking" /I "../Plugins/Runtime/SoundMod/Source/SoundMod/Classes" /I "../../../Projects/Research/CouplingTest/Intermediate/Build/Win64/CouplingTest/Inc/SoundMod" /I "../Plugins/Runtime/SoundMod/Source/SoundMod/Public" /I "../Plugins/Runtime/SoundMod/Source/SoundMod" /I "../../../Projects/Research/CouplingTest/Plugins/Coupling/Source/Coupling" /D "UE_GAME=1" /D "IS_PROGRAM=0" /D "UE_ROCKET=0" /D "UNICODE" /D "_UNICODE" /D "__UNREAL__" /D "IS_MONOLITHIC=1" /D "WITH_ENGINE=1" /D "WITH_UNREAL_DEVELOPER_TOOLS=0" /D "WITH_COREUOBJECT=1" /D "USE_STATS_WITHOUT_ENGINE=0" /D "WITH_PLUGIN_SUPPORT=0" /D "USE_LOGGING_IN_SHIPPING=0" /D "UE_BUILD_MINIMAL=1" /D "WITH_EDITOR=0" /D "WITH_EDITORONLY_DATA=0" /D "WITH_SERVER_CODE=1" /D "UBT_COMPILED_PLATFORM=Win64" /D "WIN32=1" /D "_WIN32_WINNT=0x0600" /D "WINVER=0x0600" /D "PLATFORM_WINDOWS=1" /D "NDEBUG=1" /D "UE_BUILD_DEBUG=1" /D "UE_ENGINE_DIRECTORY=../../../../../UnrealEngine/Engine/" /D "UE_PROJECT_NAME=CouplingTest" /D "ORIGINAL_FILE_NAME=\"CouplingTest-Coupling-Win64-Debug-Static.lib\"" /D "WITH_COUPLING=1" /D "WITH_COUPLING=1" /D "COUPLING_API=" /D "UE_ENABLE_ICU=1" /D "WITH_STEAMWORKS=0" /D "WITH_DIRECTXMATH=0" /D "CORE_API=" /D "COREUOBJECT_API=" /D "WITH_PHYSX=1" /D "WITH_APEX=1" /D "WITH_RUNTIME_PHYSICS_COOKING" /D "WITH_BOX2D=1" /D "WITH_RECAST=1" /D "ENGINE_API=" /D "WITH_FREETYPE=1" /D "SLATE_API=" /D "INPUTCORE_API=" /D "WITH_FREETYPE=1" /D "SLATECORE_API=" /D "MESSAGING_API=" /D "RENDERCORE_API=" /D "RHI_API=" /D "SHADERCORE_API=" /D "ASSETREGISTRY_API=" /D "ENGINEMESSAGES_API=" /D "ENGINESETTINGS_API=" /D "SYNTHBENCHMARK_API=" /D "RENDERER_API=" /D "WITH_RECAST=1" /D "AIMODULE_API=" /D "INPUTDEVICE_API=" /D "NAVMESH_API=" /D "NETWORKING_API=" /D "SOCKETS_PACKAGE=1" /D "SOCKETS_API=" /D "BUILDING_STATIC" /D "SOUNDMOD_API=" /D "_HAS_EXCEPTIONS=0" /Yu"D:\Unreal\Projects\Research\CouplingTest\Plugins\Coupling\Source\Coupling\Private\CouplingPrivatePCH.h" /Fp"D:\Unreal\Projects\Research\CouplingTest\Intermediate\Build\Win64\CouplingTest\Debug\Plugins\Static\Coupling\CouplingPrivatePCH.h.pch" /FI"D:\Unreal\Projects\Research\CouplingTest\Plugins\Coupling\Source\Coupling\Private\CouplingPrivatePCH.h" "D:\Unreal\Projects\Research\CouplingTest\Intermediate\Build\Win64\CouplingTest\Debug\Plugins\Static\Coupling\Module.Coupling.1_of_8.cpp" /Fo"D:\Unreal\Projects\Research\CouplingTest\Intermediate\Build\Win64\CouplingTest\Debug\Plugins\Static\Coupling\Module.Coupling.1_of_8.cpp.obj" /TP /GR- /W4

See my answer below. I have a partial log for you that may explain things. Couldn’t post it as a reply because of the character limit.

Compile completed and with the /Ob2 disabled, I can now step into the function. I think that including /Ob2 on the Debug compiles is a bug as inlining is an optimization. It should be /Ob0.

Do you still need an example or have I given you enough info to reproduce the problem?

Gotcha, that makes sense. We’ve had that flag on in debug builds since 2008 from what I can tell, so I’ll ask around if anyone knows why.

Hi, Ben

Should “-verbose” work in 4.5 ? I tried with 4.5.1 but for some reason the only change is the print of run time at the end, don’t see compilers options.

Thanks in advance!

Sorry, Ben. It actually work. I just make a mistake when was trying to add -verbose to build line.