When profiling a Windows container which is using Host Process Isolation (this means it is running under System account with full access to host registry, file system and other processes). The only part which comes from the docker image is the mount point c:\hpc where the container file system data for the executables are located.
In theory I should be able to profile these container from the host as is but this works only for some dlls but not all of them.
The dlls which get proper image id data can be retrieved from host during merge step are reachable from containerd snapshot:
C:\var\lib\rancher\rke2\agent\containerd\io.containerd.snapshotter.v1.windows\snapshots\413\Files\product\bin\boost date_time-mt.dll
While all other dlls are only accesible via its Vhd path:
\Device\VhdHardDisk{8cd8f3e4-93f5-47ce-83b2-78bd58522c39}\product\bin\product.dll
The "normal" approach to successfully add image id events is to do the merge step on host and all containers like
xperf -stop "NT Kernel Logger" -stop UserModeLogger
xperf -merge kernel.etl user.etl mergedOnHost.etl
foreach(containerId in containers)
{
kubectl exec containerId -i -t -- cmd /C xperf merge c:\temp\mergedOnhost.etl c:\temp\mergedinContainer.etl & del c:\temp\mergedOnhost.etl & move c:\temp\mergedinContainer.etl c:\temp\mergedOnHost.etl
}
This will also properly generate NGEN pdbs which works because the image roots are stored in host registry and c:\windows\assembly... anyway. Just the path resoluton to c:\hpc\product\bin which is the path used as Native Image root. Dialing in into each container makes the path accesible and ngen createpbd calls will succeed.
But the Vhd paths are not resolved in this case. Is this a known limitation of the merge code inside containers. Who would be responsible to fix?
I have tried to inject image id events on my own by relogging an etl but for some reason I was not able to inject anything. I was using TraceEvent library from PerfView.
internal unsafe void Inject()
{
using (var relogger = new ETWReloggerTraceEventSource(myInputEtlFile, myOutputEtlFile) { OutputUsesCompressedFormat = false })
{
bool bRelogged = false;
relogger.AllEvents += (obj) =>
{
var time = GetDateTimeForUTCToBeUsedForWriteEvent(relogger, 100);
relogger.WriteEvent(obj);
Guid ImageIdGuid = new Guid("B3E675D7-2554-4f18-830B-2762732560DE");
if (obj.TaskGuid == ImageIdGuid && (int) obj.Opcode == 64 && !bRelogged)
{
bRelogged = true;
var ed = new _EVENT_DESCRIPTOR
{
Id = 0,
Task = 0,
Opcode = 64, // Image ID Version but any other opcode does also not show up in relogged etl file
Version = 0,
Channel = 0x0,
Level = 0,
Keyword = 0,
};
for (int i = 1; i < 2000; i++)
{
relogger.WriteEvent(ImageIdGuid, ref ed, time, obj.ProcessID, 0, obj.ThreadID, obj.ActivityID,
new object[] {
1234,
0x1000,
"origFileName.dll"+i,
"file description",
"file version string"+i,
"bin file version string",
"ver_language string",
"product name",
"company Name",
"product version",
"File Id",
"Program id"
}
);
}
}
};
relogger.Process();
}
}
private DateTime GetDateTimeForUTCToBeUsedForWriteEvent(ETWReloggerTraceEventSource relogger, double relativeTimeToStartInMS)
{
Type type = typeof(TraceEventSource);
MethodInfo UTCDateTimeToQPCMethode = type.GetMethod("RelativeMSecToQPC", BindingFlags.NonPublic | BindingFlags.Instance);
var relativeTimeToStart = relativeTimeToStartInMS;
var modTime = (long)UTCDateTimeToQPCMethode.Invoke(relogger, new object[] { relativeTimeToStart });
var newTime = DateTime.FromFileTimeUtc(modTime);
return newTime;
}
When I inject events they do not show up in PerfView as I would expect them. So something is wrong, but I am not sure why.