summaryrefslogtreecommitdiffstats
path: root/chrome/test/chrome_process_util_mac.cc
blob: 4c60ad36ef35f53cfde5a779a0a11b18897e5ec1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// Copyright (c) 2009 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/test/chrome_process_util.h"

#include <string>
#include <vector>

#include "base/command_line.h"
#include "base/process_util.h"
#include "base/string_util.h"

// Yes, this is impossibly lame. This horrible hack is Good Enough, though,
// because it's not used in production code, but just for testing.
//
// We could make this better by creating a system through which all instances of
// Chromium can communicate. ProcessSingleton does that for Windows and Linux,
// but the Mac doesn't implement it as its system services handle it. It's not
// worth implementing just for this.
//
// We could do something similar to what Linux does, and use |fuser| to find a
// file that the app ordinarily opens within the data dir. However, fuser is
// broken on Leopard, and does not detect files that lsof shows are open.
//
// What's going on here is that during ui_tests, the Chromium application is
// launched using the --user-data-dir command line option. By examining the
// output of ps, we can find the appropriately-launched Chromium process. Note
// that this _does_ work for paths with spaces. The command line that ps gives
// is just the argv separated with spaces. There's no escaping spaces as a shell
// would do, so a straight string comparison will work just fine.
//
// TODO(avi):see if there is a better way

base::ProcessId ChromeBrowserProcessId(const FilePath& data_dir) {
  std::vector<std::string> argv;
  argv.push_back("ps");
  argv.push_back("-xw");

  std::string ps_output;
  if (!base::GetAppOutput(CommandLine(argv), &ps_output))
    return -1;

  std::vector<std::string> lines;
  SplitString(ps_output, '\n', &lines);

  for (size_t i=0; i<lines.size(); ++i) {
    const std::string& line = lines[i];
    if (line.find(data_dir.value()) != std::string::npos &&
        line.find("type=renderer") == std::string::npos) {
      int pid = StringToInt(line);  // pid is at beginning of line
      return pid==0 ? -1 : pid;
    }
  }

  return -1;
}

MacChromeProcessInfoList GetRunningMacProcessInfo(
    const ChromeProcessList &process_list) {
  MacChromeProcessInfoList result;

  // Build up the ps command line
  std::vector<std::string> cmdline;
  cmdline.push_back("ps");
  cmdline.push_back("-o");
  cmdline.push_back("pid=,rsz=,vsz=");  // fields we need, no headings
  ChromeProcessList::const_iterator process_iter;
  for (process_iter = process_list.begin();
       process_iter != process_list.end();
       ++process_iter) {
    cmdline.push_back("-p");
    cmdline.push_back(StringPrintf("%d", *process_iter));
  }

  // Invoke it
  std::string ps_output;
  if (!base::GetAppOutput(CommandLine(cmdline), &ps_output))
    return result;  // All the pids might have exited

  // Process the results
  std::vector<std::string> ps_output_lines;
  SplitString(ps_output, '\n', &ps_output_lines);
  std::vector<std::string>::const_iterator line_iter;
  for (line_iter = ps_output_lines.begin();
       line_iter != ps_output_lines.end();
       ++line_iter) {
    std::string line(CollapseWhitespaceASCII(*line_iter, false));
    std::vector<std::string> values;
    SplitString(line, ' ', &values);
    if (values.size() == 3) {
      MacChromeProcessInfo proc_info;
      proc_info.pid = StringToInt(values[0]);
      proc_info.rsz_in_kb = StringToInt(values[1]);
      proc_info.vsz_in_kb = StringToInt(values[2]);
      if (proc_info.pid && proc_info.rsz_in_kb && proc_info.vsz_in_kb)
        result.push_back(proc_info);
    }
  }

  return result;
}

// Common interface for fetching memory values from parsed ps output.
// We fill in both values we may get called for, even though our
// callers typically only care about one, just to keep the code
// simple and because this is a test.
static bool GetMemoryValuesHack(uint32 process_id,
                          size_t* virtual_size,
                          size_t* working_set_size) {
  DCHECK(virtual_size && working_set_size);

  std::vector<base::ProcessId> processes;
  processes.push_back(process_id);

  MacChromeProcessInfoList process_info = GetRunningMacProcessInfo(processes);
  if (process_info.empty())
    return false;

  bool found_process = false;
  *virtual_size = 0;
  *working_set_size = 0;

  MacChromeProcessInfoList::iterator it = process_info.begin();
  for (; it != process_info.end(); ++it) {
    if (it->pid != static_cast<base::ProcessId>(process_id))
      continue;
    found_process = true;
    *virtual_size = it->vsz_in_kb * 1024;
    *working_set_size = it->rsz_in_kb * 1024;
    break;
  }

  return found_process;
}

size_t ChromeTestProcessMetrics::GetPagefileUsage() {
  size_t virtual_size;
  size_t working_set_size;
  GetMemoryValuesHack(process_handle_, &virtual_size, &working_set_size);
  return virtual_size;
}

size_t ChromeTestProcessMetrics::GetWorkingSetSize() {
  size_t virtual_size;
  size_t working_set_size;
  GetMemoryValuesHack(process_handle_, &virtual_size, &working_set_size);
  return working_set_size;
}