OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 using System; |
| 6 using System.Collections.Generic; |
| 7 using System.ComponentModel; |
| 8 using System.Data; |
| 9 using System.Diagnostics; |
| 10 using System.Drawing; |
| 11 using System.IO; |
| 12 using System.Linq; |
| 13 using System.Management; |
| 14 using System.Text; |
| 15 using System.Threading.Tasks; |
| 16 using System.Windows.Forms; |
| 17 |
| 18 using ChromeDebug.LowLevel; |
| 19 |
| 20 namespace ChromeDebug { |
| 21 // The form that is displayed to allow the user to select processes to attach
to. Note that we |
| 22 // cannot interact with the DTE object from here (I assume this is because the
dialog is running |
| 23 // on a different thread, although I don't fully understand), so any access to
the DTE object |
| 24 // will have to be done through events that get posted back to the main packag
e thread. |
| 25 public partial class AttachDialog : Form { |
| 26 private class ProcessViewItem : ListViewItem { |
| 27 public ProcessViewItem() { |
| 28 Category = ProcessCategory.Other; |
| 29 MachineType = LowLevelTypes.MachineType.UNKNOWN; |
| 30 } |
| 31 |
| 32 public string Exe; |
| 33 public int ProcessId; |
| 34 public int SessionId; |
| 35 public string Title; |
| 36 public string DisplayCmdLine; |
| 37 public string[] CmdLineArgs; |
| 38 public ProcessCategory Category; |
| 39 public LowLevelTypes.MachineType MachineType; |
| 40 |
| 41 public ProcessDetail Detail; |
| 42 } |
| 43 |
| 44 private Dictionary<ProcessCategory, List<ProcessViewItem>> loadedProcessTabl
e = null; |
| 45 private Dictionary<ProcessCategory, ListViewGroup> processGroups = null; |
| 46 private List<int> selectedProcesses = null; |
| 47 |
| 48 public AttachDialog() { |
| 49 InitializeComponent(); |
| 50 |
| 51 loadedProcessTable = new Dictionary<ProcessCategory, List<ProcessViewItem>
>(); |
| 52 processGroups = new Dictionary<ProcessCategory, ListViewGroup>(); |
| 53 selectedProcesses = new List<int>(); |
| 54 |
| 55 // Create and initialize the groups and process lists only once. On a rese
t |
| 56 // we don't clear the groups manually, clearing the list view should clear
the |
| 57 // groups. And we don't clear the entire processes_ dictionary, only the |
| 58 // individual buckets inside the dictionary. |
| 59 foreach (object value in Enum.GetValues(typeof(ProcessCategory))) { |
| 60 ProcessCategory category = (ProcessCategory)value; |
| 61 |
| 62 ListViewGroup group = new ListViewGroup(category.ToGroupTitle()); |
| 63 processGroups[category] = group; |
| 64 listViewProcesses.Groups.Add(group); |
| 65 |
| 66 loadedProcessTable[category] = new List<ProcessViewItem>(); |
| 67 } |
| 68 } |
| 69 |
| 70 // Provides an iterator that evaluates to the process ids of the entries tha
t are selected |
| 71 // in the list view. |
| 72 public IEnumerable<int> SelectedItems { |
| 73 get { |
| 74 foreach (ProcessViewItem item in listViewProcesses.SelectedItems) |
| 75 yield return item.ProcessId; |
| 76 } |
| 77 } |
| 78 |
| 79 private void AttachDialog_Load(object sender, EventArgs e) { |
| 80 RepopulateListView(); |
| 81 } |
| 82 |
| 83 // Remove command line arguments that we aren't interested in displaying as
part of the command |
| 84 // line of the process. |
| 85 private string[] FilterCommandLine(string[] args) { |
| 86 Func<string, int, bool> AllowArgument = delegate(string arg, int index) { |
| 87 if (index == 0) |
| 88 return false; |
| 89 return !arg.StartsWith("--force-fieldtrials", StringComparison.CurrentCu
ltureIgnoreCase); |
| 90 }; |
| 91 |
| 92 // The force-fieldtrials command line option makes the command line view u
seless, so remove |
| 93 // it. Also remove args[0] since that is the process name. |
| 94 args = args.Where(AllowArgument).ToArray(); |
| 95 return args; |
| 96 } |
| 97 |
| 98 private void ReloadNativeProcessInfo() { |
| 99 foreach (List<ProcessViewItem> list in loadedProcessTable.Values) { |
| 100 list.Clear(); |
| 101 } |
| 102 |
| 103 Process[] processes = Process.GetProcesses(); |
| 104 foreach (Process p in processes) { |
| 105 ProcessViewItem item = new ProcessViewItem(); |
| 106 try { |
| 107 item.Detail = new ProcessDetail(p.Id); |
| 108 if (item.Detail.CanReadPeb && item.Detail.CommandLine != null) { |
| 109 item.CmdLineArgs = Utility.SplitArgs(item.Detail.CommandLine); |
| 110 item.DisplayCmdLine = GetFilteredCommandLineString(item.CmdLineArgs)
; |
| 111 } |
| 112 item.MachineType = item.Detail.MachineType; |
| 113 } |
| 114 catch (Exception) { |
| 115 // Generally speaking, an exception here means the process is privileg
ed and we cannot |
| 116 // get any information about the process. For those processes, we wil
l just display the |
| 117 // information that the Framework gave us in the Process structure. |
| 118 } |
| 119 |
| 120 // If we don't have the machine type, its privilege level is high enough
that we won't be |
| 121 // able to attach a debugger to it anyway, so skip it. |
| 122 if (item.MachineType == LowLevelTypes.MachineType.UNKNOWN) |
| 123 continue; |
| 124 |
| 125 item.ProcessId = p.Id; |
| 126 item.SessionId = p.SessionId; |
| 127 item.Title = p.MainWindowTitle; |
| 128 item.Exe = p.ProcessName; |
| 129 if (item.CmdLineArgs != null) |
| 130 item.Category = DetermineProcessCategory(item.CmdLineArgs); |
| 131 |
| 132 List<ProcessViewItem> items = loadedProcessTable[item.Category]; |
| 133 item.Group = processGroups[item.Category]; |
| 134 items.Add(item); |
| 135 } |
| 136 } |
| 137 |
| 138 // Filter the command line arguments to remove extraneous arguments that we
don't wish to |
| 139 // display. |
| 140 private string GetFilteredCommandLineString(string[] args) { |
| 141 if (args == null || args.Length == 0) |
| 142 return string.Empty; |
| 143 |
| 144 args = FilterCommandLine(args); |
| 145 return string.Join(" ", args, 0, args.Length); |
| 146 } |
| 147 |
| 148 // Using a heuristic based on the command line, tries to determine what type
of process this |
| 149 // is. |
| 150 private ProcessCategory DetermineProcessCategory(string[] cmdline) { |
| 151 if (cmdline == null || cmdline.Length == 0) |
| 152 return ProcessCategory.Other; |
| 153 |
| 154 string file = Path.GetFileName(cmdline[0]); |
| 155 if (file.Equals("delegate_execute.exe", StringComparison.CurrentCultureIgn
oreCase)) |
| 156 return ProcessCategory.DelegateExecute; |
| 157 else if (file.Equals("chrome.exe", StringComparison.CurrentCultureIgnoreCa
se)) { |
| 158 if (cmdline.Contains("--type=renderer")) |
| 159 return ProcessCategory.Renderer; |
| 160 else if (cmdline.Contains("--type=plugin") || cmdline.Contains("--type
=ppapi")) |
| 161 return ProcessCategory.Plugin; |
| 162 else if (cmdline.Contains("--type=gpu-process")) |
| 163 return ProcessCategory.Gpu; |
| 164 else if (cmdline.Contains("--type=service")) |
| 165 return ProcessCategory.Service; |
| 166 else if (cmdline.Any(arg => arg.StartsWith("-ServerName"))) |
| 167 return ProcessCategory.MetroViewer; |
| 168 else |
| 169 return ProcessCategory.Browser; |
| 170 } else |
| 171 return ProcessCategory.Other; |
| 172 } |
| 173 |
| 174 private void InsertCategoryItems(ProcessCategory category) { |
| 175 foreach (ProcessViewItem item in loadedProcessTable[category]) { |
| 176 item.SubItems.Add(item.Exe); |
| 177 item.SubItems.Add(item.ProcessId.ToString()); |
| 178 item.SubItems.Add(item.Title); |
| 179 item.SubItems.Add(item.MachineType.ToString()); |
| 180 item.SubItems.Add(item.SessionId.ToString()); |
| 181 item.SubItems.Add(item.DisplayCmdLine); |
| 182 listViewProcesses.Items.Add(item); |
| 183 } |
| 184 } |
| 185 |
| 186 private void AutoResizeColumns() { |
| 187 // First adjust to the width of the headers, since it's fast. |
| 188 listViewProcesses.AutoResizeColumns(ColumnHeaderAutoResizeStyle.HeaderSize
); |
| 189 |
| 190 // Save the widths so we can use them again later. |
| 191 List<int> widths = new List<int>(); |
| 192 foreach (ColumnHeader header in listViewProcesses.Columns) |
| 193 widths.Add(header.Width); |
| 194 |
| 195 // Now let Windows do the slow adjustment based on the content. |
| 196 listViewProcesses.AutoResizeColumns(ColumnHeaderAutoResizeStyle.ColumnCont
ent); |
| 197 |
| 198 // Finally, iterate over each column, and resize those columns that just g
ot smaller. |
| 199 listViewProcesses.Columns[0].Width = 0; |
| 200 int total = 0; |
| 201 for (int i = 1; i < listViewProcesses.Columns.Count; ++i) { |
| 202 // Resize to the largest of the two, but don't let it go over a pre-defi
ned maximum. |
| 203 int max = Math.Max(listViewProcesses.Columns[i].Width, widths[i]); |
| 204 int capped = Math.Min(max, 300); |
| 205 |
| 206 // We do still want to fill up the available space in the list view howe
ver, so if we're |
| 207 // under then we can fill. |
| 208 int globalMinWidth = listViewProcesses.Width - SystemInformation.Vertica
lScrollBarWidth; |
| 209 if (i == listViewProcesses.Columns.Count - 1 && (total + capped) < (glob
alMinWidth - 4)) |
| 210 capped = globalMinWidth - total - 4; |
| 211 |
| 212 total += capped; |
| 213 listViewProcesses.Columns[i].Width = capped; |
| 214 } |
| 215 } |
| 216 |
| 217 private void RepopulateListView() { |
| 218 listViewProcesses.Items.Clear(); |
| 219 |
| 220 ReloadNativeProcessInfo(); |
| 221 |
| 222 InsertCategoryItems(ProcessCategory.Browser); |
| 223 InsertCategoryItems(ProcessCategory.Renderer); |
| 224 InsertCategoryItems(ProcessCategory.Gpu); |
| 225 InsertCategoryItems(ProcessCategory.Plugin); |
| 226 InsertCategoryItems(ProcessCategory.MetroViewer); |
| 227 InsertCategoryItems(ProcessCategory.Service); |
| 228 InsertCategoryItems(ProcessCategory.DelegateExecute); |
| 229 if (!checkBoxOnlyChrome.Checked) |
| 230 InsertCategoryItems(ProcessCategory.Other); |
| 231 |
| 232 AutoResizeColumns(); |
| 233 } |
| 234 |
| 235 private void buttonRefresh_Click(object sender, EventArgs e) { |
| 236 RepopulateListView(); |
| 237 } |
| 238 |
| 239 private void buttonAttach_Click(object sender, EventArgs e) { |
| 240 System.Diagnostics.Debug.WriteLine("Closing dialog."); |
| 241 this.Close(); |
| 242 } |
| 243 |
| 244 private void checkBoxOnlyChrome_CheckedChanged(object sender, EventArgs e) { |
| 245 if (!checkBoxOnlyChrome.Checked) |
| 246 InsertCategoryItems(ProcessCategory.Other); |
| 247 else { |
| 248 foreach (ProcessViewItem item in loadedProcessTable[ProcessCategory.Othe
r]) { |
| 249 listViewProcesses.Items.Remove(item); |
| 250 } |
| 251 } |
| 252 } |
| 253 } |
| 254 } |
OLD | NEW |