dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.16k stars 4.71k forks source link

Get process description from dll info #2688

Open livarcocc opened 7 years ago

livarcocc commented 7 years ago

From @benaadams on April 9, 2017 18:15

Currently if you run a dotnet program with the shared host its listing in Task manager isn't particularly informative:

image

They all look the same; with more and more adoption will get harder to distinguish between portable dotnet apps.

It would be better if it took the description from the entry point dll. While I'm not sure if there is an api to change the running process name; svchost seems to manage it, so it may be possible:

image

Copied from original issue: dotnet/cli#6279

danmoseley commented 7 years ago

@livarcocc how is this a setup issue? wouldn't it be dotnet.exe that would set its own process name (if such a thing is possible)?

mellinoe commented 7 years ago

@danmosemsft dotnet.exe is produced from core-setup.

danmoseley commented 7 years ago

Ah that makes sense.

mstum commented 7 years ago

So I know that CoreRT is kinda intended to solve the "Let's make a monolithic executable with no dependencies" scenario, but I do wonder if it makes sense to explore the idea of "Let's create a monolithic, big executable (thing static linking in C++)" here, without worrying about the AOT/.net Native part that CoreRT tries to solve.

But that might be an entirely different issue altogether.

benaadams commented 7 years ago

@mstum il-repack might do this? https://github.com/dotnet/corefx/issues/11672#issuecomment-295844213

axel-habermaier commented 7 years ago

This is really annoying: We have an application that consists of multiple different processes, which we're considering to update to .NET core. However, we're having several issues stopping us from doing so at the moment, one of which is the fact that all processes would now be called 'dotnet' or 'apphost' when using self contained deployment. This reduces the user experience. In particular, application crashes now show "dotnet stopped working", which is completely surprising for our users, as they don't even know (nor care) what "dotnet" is.

Additionally, it makes debugging harder. Due to the fact that native code debugging of .NET core 2.0 projects is not supported by VS at the moment, we have to manually attach the debugger to the process we want to debug -- the fact that all processes are just called "dotnet" doesn't really help, as you can imagine.

mellinoe commented 7 years ago

I agree that we should do something here. @gkhanna79 Can we figure out a plan for the next release cycle about this?

benaadams commented 7 years ago

From https://github.com/dotnet/coreclr/issues/13340

I'm creating a program that needs to get a list of currently executing processes in memory and take action to kill a given process, if it's a different instance of the same (currently executing program) that has become hung for some reason; where I have a criteria based on start time to declare a given process as being hung. This logic is being developed on a windows machine using "dotnet" core, then packaged and run on a Ubuntu machine.

If I get a list of processes via "Process.GetProcesses", I can see my currently executing logic in the list as "dotnet". This isn't real helpful, as what if there are X different "dotnet" programs running on this machine, they all look like they are the same logic due to the "dotnet" name. Some of them MIGHT be hung instances of the logic I'm after, some of them MIGHT be totally other "dotnet" assemblies.

rmja commented 6 years ago

@axel-habermaier for the debugging part, as a workaround, you can start dotnet with the start command, e.g.:

start "My App" dotnet MyApp.dll

The "My App" text will show in the Title column when attaching to a process in VS

steveharter commented 6 years ago

See also https://github.com/dotnet/core-setup/issues/2817

Some options depending on scenario: 1) Add Task Manager extension to list app name (need to research this more to see if currently possible) 2) Support a named exe (like standalone exe) but can use the shared framework

ehsaanwelcome commented 6 years ago

After updated to .net core 2.0 I do not see app name in process list (only dotne.exe) while attaching debugger in VS. If i do trace the right process, VS do not re-attach process after rebuilding as there are multiple dotnet.exe processes. Before, it gets easily re-attach in debug menu by having app name.

steveharter commented 6 years ago

@ehsaanwelcome before 2.0, where did you see the app name in the VS debugger attach dialog? In the Process column or Title column?

Note if you have a standalone app (or "self contained application") then it will be named <myapp>.exe and will appear as Process (process name) in the attach dialog, otherwise you'll have a portable app (or "framework dependent application") which Process will always be dotnet.exe.

Also to help with your debugging you could use the tip from @tmja that will show a string of your choice in Title column: start "My App" dotnet MyApp.dll

ehsaanwelcome commented 6 years ago

@steveharter, consider following scenario VS2017 Net461 dotnet watch run

  1. VS => debug => attach to process (each dotnet process shows app name as process name)
  2. change code
  3. recompile by dotnet watch (new process id is assigned to process)
  4. VS => debug => re-attach to process (no need to repeat step 1 as process name is same although process id is different therefore VS detect the right process by name and attach process automatically)

Now change Net461 to netcoreapp2.0

  1. VS => debug => attach to process (each dotnet process shows dotnet.exe so i have to first trace the right process in task manager)
  2. change code
  3. recompile by dotnet watch (new process id is assigned to process)
  4. VS => debug => re-attach to process won't work so VS redirects to step 1 again which means i have to again trace the right process

in first scenario debugging was very easier, change code => save it => dotnet watch run trigger compilation => VS re-attach => debugger attach sucessfully

danmoseley commented 6 years ago

@steveharter can we set the Title even if we can't change the (process) Name? In VS, the Title shows up as the path to dotnet.exe. If we can set it to the parameters given to dotnet.exe (after dotnet exec maybe and with path stripped so it's just a dll name) that would help a lot.

steveharter commented 6 years ago

The Title can't be changed in a running process unless it is a GUI app. From the host, we could change the current console's title, but that doesn't really work since it would be too late. It is too late because the title would be changed after the process starts (VS would still show the previous title), and would be wrong when launching another app later.

However, if we leverage the functionality planned in 3.0 for GUI apps https://github.com/dotnet/core-setup/issues/196 to add metadata for non-GUI apps as well then the Title etc would be present in the framework-dependent .exe.

Setting 3.0 milestone to assume this for now.

jceddy commented 5 years ago

@rmja

for the debugging part, as a workaround, you can start dotnet with the start command, e.g.:


start "My App" dotnet MyApp.dll

Unfortunately this doesn't work when specifying what to start in web.config for IIS

jeffschwMSFT commented 5 years ago

With apphost being the default, I feel this situation is much better. But the underlying ask of having details show for dotnet.exe launched apps is still open.

ghost commented 5 years ago

The Title can't be changed in a running process unless it is a GUI app. From the host, we could change the current console's title, but that doesn't really work since it would be too late. It is too late because the title would be changed after the process starts (VS would still show the previous title), and would be wrong when launching another app later.

However, if we leverage the functionality planned in 3.0 for GUI apps dotnet/runtime#2455 to add metadata for non-GUI apps as well then the Title etc would be present in the framework-dependent .exe.

Setting 3.0 milestone to assume this for now.

I can confirm this works. In fact, on Windows (but I think the 3.0 GUI stuff only works on Windows too) you can already use the existing Win32 API to create a window with a title. I'm using this, with a #if DEBUG block around it.

#if DEBUG
    delegate IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
    class DebuggerForm
    {
        const UInt32 WS_EX_NOACTIVATE = 0x08000000;
        const UInt32 WS_MINIMIZE = 0x20000000;
        const int SW_SHOWMINNOACTIVE = 7;
        const UInt32 WM_DESTROY = 2;

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        struct WNDCLASSEX
        {
            [MarshalAs(UnmanagedType.U4)]
            public int cbSize;
            [MarshalAs(UnmanagedType.U4)]
            public int style;
            public IntPtr lpfnWndProc;
            public int cbClsExtra;
            public int cbWndExtra;
            public IntPtr hInstance;
            public IntPtr hIcon;
            public IntPtr hCursor;
            public IntPtr hbrBackground;
            public string lpszMenuName;
            public string lpszClassName;
            public IntPtr hIconSm;
        }

        private WndProc delegWndProc = myWndProc;

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

        [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
        static extern bool DestroyWindow(IntPtr hWnd);

        [DllImport("user32.dll", SetLastError = true, EntryPoint = "CreateWindowEx")]
        public static extern IntPtr CreateWindowEx(UInt32 dwExStyle, UInt16 lpClassName, string lpWindowName, UInt32 dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, IntPtr hMenu, IntPtr hInstance, IntPtr lpParam);

        [DllImport("user32.dll", SetLastError = true, EntryPoint = "RegisterClassEx")]
        static extern System.UInt16 RegisterClassEx([In] ref WNDCLASSEX lpWndClass);

        [DllImport("kernel32.dll")]
        static extern uint GetLastError();

        [DllImport("user32.dll")]
        static extern IntPtr DefWindowProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);

        internal bool Create(string name)
        {
            WNDCLASSEX windowClass = new WNDCLASSEX();
            windowClass.cbSize = Marshal.SizeOf(typeof(WNDCLASSEX));
            windowClass.style = 0;
            windowClass.hbrBackground = IntPtr.Zero;
            windowClass.cbClsExtra = 0;
            windowClass.cbWndExtra = 0;
            windowClass.hInstance = Marshal.GetHINSTANCE(this.GetType().Module);
            windowClass.hIcon = IntPtr.Zero;
            windowClass.hCursor = IntPtr.Zero;
            windowClass.lpszMenuName = null;
            windowClass.lpszClassName = "DebuggerForm";
            windowClass.lpfnWndProc = Marshal.GetFunctionPointerForDelegate(delegWndProc);
            windowClass.hIconSm = IntPtr.Zero;
            ushort regResult = RegisterClassEx(ref windowClass);

            if (regResult == 0)
            {
                uint error = GetLastError();
                return false;
            }
            string wndClass = windowClass.lpszClassName;

            IntPtr hWnd = CreateWindowEx(WS_EX_NOACTIVATE, regResult, name, WS_MINIMIZE, 0, 0, 200, 200, IntPtr.Zero, IntPtr.Zero, windowClass.hInstance, IntPtr.Zero);

            if (hWnd == ((IntPtr)0))
            {
                uint error = GetLastError();
                return false;
            }
            ShowWindow(hWnd, SW_SHOWMINNOACTIVE);

            return true;
        }

        private static IntPtr myWndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
        {
            switch (msg)
            {
                case WM_DESTROY:
                    DestroyWindow(hWnd);
                    break;

                default:
                    break;
            }
            return DefWindowProc(hWnd, msg, wParam, lParam);
        }
    }
#endif

Now simply call this in your Startup.cs and you're all set.

#if DEBUG
            new DebuggerForm().Create("WebRunner");
#endif

This has 1 minor disadvantage: somehow the message loop doesn't work quite as expected. Your dotnet process will show up as "Not Responding" in the task manager. The window itself is completely hidden and web requests work as expected. Since this is not really an issue for me I haven't spent any time trying to fix it, though I'm sure it could be done.

image The correct dotnet.exe is now named "WebRunner".

I'm basically waiting for 3.0 to replace the ugly Win32 API with something that's less ugly, but either way it's a bit of a hack.

burkenyo commented 4 years ago

I build portable .net core apps on a Mac and run them on desktops and in containers . Being able to distinguish the various “dotnet” processes would be very helpful!

warrenlbrown commented 4 years ago

On a Mac, without being able to distinguish the processes in .Net Core 3.1, we cannot monitor our own processes to determine whether to stop, start, etc. them. This seems like a big hole, and I cannot see anyway around it using System.Diagnostics Process stuff. StartInfo is n/a, and the process name (the DLL name) is not available anywhere I can see when trying to monitor these processes from another. One hack (and VERY ugly) I can think of is to write the application.DLL name and associated dotnet pid to a temp file in some agreed upon location. Another which only applies to *nix and Mac, would be to use the output of the ps command, which given the nature of the application is not a pretty way to do things either. Does anybody have any idea what MS plans are in this area? (Kind of like their omission of named/global Mutex and EventWaitHandle.) Thank you.

shinkathe commented 2 years ago

image

VScode attaching looks like this atm. Pure guesswork needed.

colotiline commented 1 year ago

I add PID (Process.GetCurrentProcess().Id) to app startup logs as a workaround.