d6205a2d4d14f4f1a9f53a695f27a26cdfd5a3a5
[mono.git] / mcs / class / Mono.Management / Mono.Attach / VirtualMachine.cs
1 using System;
2 using System.Collections.Generic;
3 using System.Diagnostics;
4 using System.Runtime.InteropServices;
5 using System.Text;
6 using System.IO;
7 using System.Threading;
8 using System.Net;
9 using System.Net.Sockets;
10
11 using Mono.Unix;
12 using Mono.Unix.Native;
13
14 namespace Mono.Attach
15 {
16         /*
17          * Represents a running mono virtual machine.
18          */
19         public class VirtualMachine {
20
21                 long pid;
22
23                 public VirtualMachine (long pid) {
24                         // FIXME: Check for unix
25                         this.pid = pid;
26                 }
27
28                 public long Pid {
29                         get {
30                                 return pid;
31                         }
32                 }
33
34                 public bool IsCurrent {
35                         get {
36                                 return pid == Syscall.getpid ();
37                         }
38                 }
39
40                 public string[] GetCommandLine () {
41                         return File.OpenText ("/proc/" + pid + "/cmdline").ReadToEnd ().Split ('\0');
42                 }
43
44                 public string GetWorkingDirectory () {
45                         int len = 256;
46
47                         while (true) {
48                                 StringBuilder sb = new StringBuilder (len);
49
50                                 int res = Syscall.readlink ("/proc/" + pid + "/cwd", sb);
51                                 if (res == -1)
52                                         throw new IOException ("Syscall.readlink () failed with error " + res + ".");
53                                 else if (res == len) {
54                                         len = len * 2;
55                                 } else {
56                                         return sb.ToString ();
57                                 }
58                         }
59                 }
60
61                 /*
62                  * Return the list of running mono vms owned by the current user. The 
63                  * result includes the current vm too.
64                  */
65                 public static List<VirtualMachine> GetVirtualMachines () {
66                         PerformanceCounterCategory p = new PerformanceCounterCategory (".NET CLR JIT");
67                         string[] machines = p.GetInstanceNames ();
68
69                         var res = new List<VirtualMachine> ();
70
71                         foreach (string s in machines) {
72                                 // The names are in the form 'pid/name'
73                                 int pos = s.IndexOf ('/');
74                                 if (pos != -1)
75                                         res.Add (new VirtualMachine (Int32.Parse (s.Substring (0, pos))));
76                         }
77                         return res;
78                 }
79
80                 /*
81                  * Loads the specific agent assembly into this vm.
82                  */
83                 public void Attach (string agent, string args) {
84                         string user = UnixUserInfo.GetRealUser ().UserName;
85
86                         // Check whenever the attach socket exists
87                         string socket_file = "/tmp/mono-" + user + "/.mono-" + pid;
88
89                         if (!File.Exists (socket_file)) {
90                                 string trigger_file = "/tmp/.mono_attach_pid" + pid;
91                                 FileStream trigger = null;
92
93                                 try {
94                                         trigger = File.Create (trigger_file);
95                                         trigger.Close ();
96
97                                         // Ask the vm to start the attach mechanism
98                                         Syscall.kill ((int)pid, Signum.SIGQUIT);
99
100                                         // Wait for the socket file to materialize
101                                         int i;
102                                         for (i = 0; i < 10; ++i) {
103                                                 if (File.Exists (socket_file))
104                                                         break;
105                                                 Thread.Sleep (100);
106                                         }
107
108                                         if (i == 10)
109                                                 throw new Exception (String.Format ("Runtime failed to create attach socket '{0}'.", socket_file));
110                                 } finally {
111                                         File.Delete (trigger_file);
112                                 }
113                         }
114
115                         /* 
116                          * We communicate with the agent inside the runtime using a simlified
117                          * version of the .net remoting protocol.
118                          */
119
120                         string path = "/tmp/mono-" + user + "/.mono-" + pid;
121
122                         UnixClient client = new UnixClient (path);
123
124                         NetworkStream stream = client.GetStream ();
125
126                         // Compose payload
127                         MemoryStream ms = new MemoryStream ();
128                         BinaryWriter writer = new BinaryWriter (ms);
129                         write_string (writer, "attach");
130                         write_string (writer, agent);
131                         write_string (writer, args);
132
133                         // Write header
134                         byte[] magic = new byte [] { (byte)'M', (byte)'O', (byte)'N', (byte)'O', 1, 0 };
135                         stream.Write (magic, 0, magic.Length);
136
137                         // Write payload length
138                         new BinaryWriter (stream).Write ((int)ms.Length);
139
140                         // Write payload
141                         stream.Write (ms.GetBuffer (), 0, (int)ms.Length);
142                 }
143
144                 enum PrimitiveType : byte {
145                         PRIM_TYPE_NULL = 17,
146                         PRIM_TYPE_STRING = 18
147                 };
148
149                 void write_string (BinaryWriter writer, string s) {
150                         if (s == null)
151                                 writer.Write ((sbyte)PrimitiveType.PRIM_TYPE_NULL);
152                         else {
153                                 writer.Write ((sbyte)PrimitiveType.PRIM_TYPE_STRING);
154                                 writer.Write (s);
155                         }
156                 }
157
158                 public override string ToString () {
159                         return "VirtualMachine (pid=" + pid + ")";
160                 }
161         }
162 }