Jump to content

KSP Plugin debugging and profiling for Visual Studio and Monodevelop on all OS


Recommended Posts

 

Edit: Those instruction are partially outdated for KSP 1.8. Have a look at the post following this one and specifically this one if you are on Windows.

 

As we all know debugging plugin in KSP is mostly done with endless spam of Debug.Log or a bunch of custom windows we have to build. We also have angavrilov method but it require to recompile the whole mono and is a bit scary. So here is the result of a couple of days of experiment on my side to get a simple method working.

So how do we get debugging to work for KSP ? Simply by using the tool that unity provides us. Unity has a debugging system for games that runs in development mode, which is not the case of KSP by default.

So here is how to get that (Linux users read that part and I will explain the difference for you further down. OSX user read this post)

KSP

  • First you need a Unity installation (or here if the version ends with px) using the exact same version as your current KSP (For 1.1 you need Unity 5.2.4f1). That version is in the first line of the output_log.txt/Player.log.
  • Go to your Unity install for that version ( "C:\Program Files\Unity\Editor" most likely) and then go in the "Data\PlaybackEngines\windowsstandalonesupport\Variations\win64_development_mono" sub-directory. Copy the "player_win.exe" file to your KSP dev install (next to KSP.exe)
  • Rename that "player_win.exe" to "KSP_x64_Dbg.exe"
  • Open a command prompt in that directory (shift right clic in the empty space of the explorer and select "open command window here". In it enter the command "mklink /J KSP_x64_Dbg_Data KSP_x64_Data". You will now have a KSP_x64_Dbg_Data that points to the stock KSP_x64_Data.
  • Get this file and put it in the KSP_x64_Data directory (it makes the mono debugger starts its listening port)
  • Launch KSP with the KSP_x64_Dbg.exe and check that you have "Development Build" in your lower right corner
  • Get this zip and put its content somewhere. For me it will be "C:\Games\Tools\pdb2mdb"
  • If you compile with VS you have to generate the mono debug information. To generate them I add some command in the project post-build event command line. Here is what mine looks like for MJ
"C:\Games\Tools\pdb2mdb\pdb2mdb.exe" "$(TargetFileName)"
xcopy /Y "$(TargetPath)" "C:\Games\ksp-win_dev\GameData\MechJeb2\Plugins\"
xcopy /Y "$(TargetDir)$(TargetName).pdb" "C:\Games\ksp-win_dev\GameData\MechJeb2\Plugins\"
xcopy /Y "$(TargetDir)$(TargetName).dll.mdb" "C:\Games\ksp-win_dev\GameData\MechJeb2\Plugins\"
  • Compile your plugin in "Debug" configuration to get the "MyPlugin.dll" AND the "MyPlugin.dll.mdb" (Generated with pdb2mdb from "MyPlugin.pdb" if you use VS. Or by mono if you use MonoDevelop or mono cmd line). Copy them in your usual GameData dir if your post build command does not already do it

We are now ready on KSP side.

Visual Studio

You need a version that allows plugin. So either a VS Pro+ or Visual Studio Community (it is free and Unity most likely already installed it). Make sure you check the Tools for Unity and NET 3.5 options in the install.

Once you have that you will have a "Attach Unity Debugger" in the Debug menu. So launch KSP and use that menu to attach VS to it. You should get an orange bar at the bottom of VS and the context should switch to debugging. You now have all the usual debugging tools (breakpoints, step by step, watches). You may want to activate "exception support" to have a the debugger break on exception (Tools => Options => "Tools for Unity" => "Exception Support")

MonoDevelop

In the "Run" menu you have "Attach to Process". It opens a windows where you should have "Unity Debugger" selected on the lower part and you can select your KSP process to attach to. It should switch to the debugging context

Profiling

Now that KSP use Unity 5 we can use the Unity Profiler for free. Launch KSP in développer mode and launch an empty Unity project. In the "Window" menu select Profiler. The profiler opens and in the "Active Profiler" at the top of the window you can select your running KSP instance. 

By default you will only see the MonoBehavior methods (Update, FixedUpdate, ...) but you can add calls in your code to profile anything. First make sure your code is compiled with the "ENABLE_PROFILER" conditional (add it in the build config). Then in your code add pairs of "Profiler.BeginSample("MyLabel");" and "Profiler.EndSample();". Be aware that if a frame take too long the profiler skip it, so you can't profile really slow method that last a single frame.An example from MJ

Profiler.BeginSample("vesselState");
ready = vesselState.Update(vessel);
Profiler.EndSample();

The profiler will now show you info for this exact call somewhere in its hierarchy. (there is a search field to find it). 

Profiler.png

 

A warning : profiling is complex and result are often more complex to interpret than you may think.

 

Linux and OSX

The steps are the same. The only difference is the directory where you get the executable. If you are on Linux you will just need to copy some files, so use a VM to install Unity or ask a friend.

- OSX :  <Unity.app>/Contents/PlaybackEngines/MacStandaloneSupport/Variations/universal_development_mono/UnityPlayer.app

- Linux : <Unity>/Data/PlaybackEngines/linuxstandalonesupport/linux32_withgfx_development or linux64_withgfx_development. Get the LinuxPlayer file, copy it to your KSP install and rename it KSP.x86 or KSP.x86_64 depending on the directory you got it from and make sure you set it +x

Missing Unity debugger for MonoDevelop (Custom install or Linux)

If you installed MonoDevelop yourself you most likely do not have the Unity Debugger installed.

Go in Tools->Addin Manager and install the Unity Soft Debugger addon

(I did not test those steps so I would like some feedback)

Edited by sarbian
Link to comment
Share on other sites

I for one welcome our new debugging overlord.

Thanks, may this type of code be gone forever!

private void ProcessNewVesselMessages()

{

int throwLine = 0;

try

{

throwLine = 1;

Dictionary<Guid, double> removeList = new Dictionary<Guid, double>();

throwLine = 2;

lock (vesselRemoveQueue)

{

throwLine = 3;

foreach (KeyValuePair<Guid, Queue<VesselRemoveEntry>> vesselRemoveSubspace in vesselRemoveQueue)

{

throwLine = 4;

while (vesselRemoveSubspace.Value.Count > 0 ? (vesselRemoveSubspace.Value.Peek().planetTime < Planetarium.GetUniversalTime()) : false)

{

throwLine = 5;

VesselRemoveEntry removeVessel = vesselRemoveSubspace.Value.Dequeue();

throwLine = 6;

RemoveVessel(removeVessel.vesselID, removeVessel.isDockingUpdate, removeVessel.dockingPlayer);

throwLine = 7;

removeList[removeVessel.vesselID] = removeVessel.planetTime;

throwLine = 8;

}

}

}

throwLine = 9;

foreach (KeyValuePair<string, Queue<KerbalEntry>> kerbalProtoSubspace in kerbalProtoQueue)

{

throwLine = 10;

while (kerbalProtoSubspace.Value.Count > 0 ? (kerbalProtoSubspace.Value.Peek().planetTime < Planetarium.GetUniversalTime()) : false)

{

throwLine = 11;

KerbalEntry kerbalEntry = kerbalProtoSubspace.Value.Dequeue();

throwLine = 12;

LoadKerbal(kerbalEntry.kerbalNode);

throwLine = 13;

}

}

throwLine = 14;

foreach (KeyValuePair<Guid, Queue<VesselProtoUpdate>> vesselQueue in vesselProtoQueue)

{

throwLine = 15;

VesselProtoUpdate vpu = null;

throwLine = 16;

//Get the latest proto update

while (vesselQueue.Value.Count > 0 ? (vesselQueue.Value.Peek().planetTime < Planetarium.GetUniversalTime()) : false)

{

throwLine = 17;

VesselProtoUpdate newVpu = vesselQueue.Value.Dequeue();

throwLine = 18;

if (newVpu != null)

{

throwLine = 19;

//Skip any protovessels that have been removed in the future

if (removeList.ContainsKey(vesselQueue.Key) ? removeList[vesselQueue.Key] < vpu.planetTime : true)

{

throwLine = 20;

vpu = newVpu;

throwLine = 21;

}

}

}

throwLine = 22;

//Apply it if there is any

if (vpu != null ? vpu.vesselNode != null : false)

{

throwLine = 23;

LoadVessel(vpu.vesselNode, vpu.vesselID);

throwLine = 24;

}

}

throwLine = 25;

foreach (KeyValuePair<Guid, Queue<VesselUpdate>> vesselQueue in vesselUpdateQueue)

{

throwLine = 26;

VesselUpdate vu = null;

throwLine = 27;

if (vesselQueue.Value == null)

{

Debug.Log("LINE27THROW vesselQueue.Value is null!");

}

if (vesselQueue.Value.Count > 0)

{

if (vesselQueue.Value.Peek() == null)

{

Debug.Log("LINE27THROW vesselQueue.Value.Peek() is null!");

continue;

}

}

//Get the latest position update

while (vesselQueue.Value.Count > 0 ? (vesselQueue.Value.Peek().planetTime < Planetarium.GetUniversalTime()) : false)

{

throwLine = 28;

vu = vesselQueue.Value.Dequeue();

throwLine = 29;

}

throwLine = 30;

//Apply it if there is any

if (vu != null)

{

throwLine = 31;

HackyInAtmoLoader.fetch.SetVesselUpdate(vesselQueue.Key, vu);

throwLine = 32;

vu.Apply();

throwLine = 33;

}

}

}

catch (Exception e)

{

DarkLog.Debug("VESSEL UPDATE THROW: Threw on line " + throwLine + ", exception: " + e);

}

}

Link to comment
Share on other sites

Oh wow. This will be absolutely fantastic. So much better than that old project where you had to build a modified version of the Mono libraries.

Edit: I'm stupid. You linked the archives.

- - - Updated - - -

Actually, could you email me the linux executables? I'm in the middle of a project with Unity 4.6 and don't particularly want to risk breaking my dev environment by reverting Unity and then going back

Link to comment
Share on other sites

Okay, so, just my luck it doesn't work for me...

Did everything you said, got the "development version" watermark in the KSP window and everything. However, nothing happens when I try to attach to the process (I'm guessing it failed to attach). However, it sometimes opens the developer console with a long list of the same message about failing to wait on a semaphore (something which seemed to be mentioned in angavrilov's commit log for his debugger. If i try to set a breakpoint anywhere, a couple seconds later mono pops up a dialog with "Debug operation failed." I verfied that in this case I hit attach before the connection timed out.

This is what the executable spits out for me as well.


Set current directory to /home/nathaniel/Projects/ksp/0.90.0
Found path: /home/nathaniel/Projects/ksp/0.90.0/KSP.x86_64
Mono path[0] = '/home/nathaniel/Projects/ksp/0.90.0/KSP_Data/Managed'
Mono path[1] = '/home/nathaniel/Projects/ksp/0.90.0/KSP_Data/Mono'
Mono config path = '/home/nathaniel/Projects/ksp/0.90.0/KSP_Data/Mono/etc'
PlayerConnection initialized from /home/nathaniel/Projects/ksp/0.90.0/KSP_Data (debug = 0)
PlayerConnection initialized network socket : 0.0.0.0 55502
Multi-casting "[IP] 192.168.1.69 [Port] 55502 [Flags] 3 [Guid] 661409256 [EditorId] 803286173 [Version] 1048832 [Id] LinuxPlayer(192.168.1.69) [Debug] 1" to [225.0.0.222:54997]...
Waiting for connection from host on [192.168.1.69:55502]...
Timed out. Continuing without host connection.
Using monoOptions --debugger-agent=transport=dt_socket,embedding=1,defer=y,address=0.0.0.0:56256
PlayerConnection already initialized - listening to [192.168.1.69:55502]

Edited by Teknoman117
Link to comment
Share on other sites

I am afraid I can't help much here. Others reported that it works for them on Linux. Try switching the mono lib for the one that angavrilov built, his fix may help and you should still get the easier debugging.

Link to comment
Share on other sites

  • 4 weeks later...

No point to sticky it since it does not seems to work with 1.0 (at least it did not in exp, I did not test with the release). I'll have a go at making it work again soonish since it is hard to got back to printing stuff once you had better.

Edited by sarbian
Link to comment
Share on other sites

Has someone managed to get it working again?

Using the development version of the unity player causes a bunch of errors on startup and prevents ksp from starting up.

I tried it on KSP version 1.0.2.842 with Unity version 4.6.4f1.

Error messages:

Serialization depth limit exceeded at 'ThermalLink'. There may be an object composition cycle in one or more of your serialized classes.

Serialization depth limit exceeded at 'OcclusionData'. There may be an object composition cycle in one or more of your serialized classes.

and lots of these:

ArgumentException: PropertyToID can only be called from the main thread.

Constructors and field initializers will be executed from the loading thread when loading a scene.

Don't use this function in the constructor or field initializers, instead move initialization code to the Awake or Start function.

at (wrapper managed-to-native) UnityEngine.Shader:PropertyToID (string)

at HighLogic..cctor () [0x00000] in <filename unknown>:0

Rethrow as TypeInitializationException: An exception was thrown by the type initializer for HighLogic

Link to comment
Share on other sites

Holy crap, I got it to work. I used Cecil to patch the assembly and fix the exceptions. Removed the static initializers and moved them to Awake() like the exceptions suggested. I still can't quite believe that actually worked.

Run this in KSP_Data\Managed, it will patch Assembly-CSharp.dll: https://dl.dropboxusercontent.com/u/7121093/ksp-mods/KSP%5B1.0.2%5DKspDevModePatcher%5BMay07%5D.zip

Source and license included.

After that it just works as described in OP. The errors about deserialization with ThermalLink and OcclusionData are probably just red herrings. I get those even in release mode.

There were a few more exceptions that only popped up later in-game. So there might be a few more I didn't run into yet, but they can probably be fixed the same way.

Link to comment
Share on other sites

I was just looking into doing that ! Great to see that it works. I know where the other change are needed and I ll have an other look tomorrow :)

Thanks !

Edit : You missed MapView ModuleAblator PQSMod_MaterialQuadRelative SkySphereControl. I ll fix that :)

The deserialization errors are real problems but as you said they are in release too.

Edit2 : I could not go to sleep with so near. Get the updated full tool to patch the dll.

Again many thanks to FW Industries :)

Edited by sarbian
Link to comment
Share on other sites

Amazing, testing asap.

KSP loads into development build now on 1.0.

Can't wait to test out when i get home. Attach unity debugger atleast connects in VSS.

*Edit, my god, it's full of breakpoints :D

Edited by TAz00
Tested it
Link to comment
Share on other sites

It sort of works alright. I did get an error whenever i try to step into this piece of code. I can break on the function call, but it errors out in the debug log as soon as you step into it. And code just continues without input. Until function breakpoint is triggered again.

  private void CloneCurrentVessel()
{
Vessel original = FlightGlobals.ActiveVessel;
ProtoVessel pv = new ProtoVessel(original);
Guid vesselId = pv.vesselID;
//Offending line
Vessel newVessel = FlightGlobals.Vessels.Where(i => i.protoVessel.vesselID.Equals(vesselId)).First();

}

[Exception]: TypeLoadException: Could not load type 'System.Func`2' from assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089'.

I've tried compiling with the modified assembly and using the regular one. All in Visual Studio 2013 sp4.

Edited by TAz00
Process of elimination found 1 line of code
Link to comment
Share on other sites

hmmm, is this fix targeted at .NET 4.0 by any chance?

I'm getting this in my .NET 3.5 project:

warning MSB3258: The primary reference "Assembly-CSharp, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL" could not be resolved because it has an indirect dependency on the .NET Framework assembly "mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" which has a higher version "4.0.0.0" than the version "2.0.0.0" in the current target framework.

and then a bunch of KSP types won't load

error CS0246: The type or namespace name 'PartModule' could not be found (are you missing a using directive or an assembly reference?)

(etc etc.)

If I target .NET 4.0, it builds fine, but then KSP won't load my plugin.

EDIT: recompiled the patcher for .net3.5 and it works like a charm :)

Edited by parachutingturtle
added details
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...