summaryrefslogtreecommitdiffstats
path: root/chrome/browser/caps/generate_state_json.cc
blob: f876aa636223882933c67619b557a36df021e650 (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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
// Copyright 2015 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/browser/caps/generate_state_json.h"

#include <stddef.h>
#include <stdint.h>

#include <string>
#include <utility>

#include "base/bind.h"
#include "base/cpu.h"
#include "base/files/file.h"
#include "base/json/json_writer.h"
#include "base/location.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/stringprintf.h"
#include "base/sys_info.h"
#include "base/thread_task_runner_handle.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "base/values.h"
#include "build/build_config.h"
#include "chrome/browser/task_manager/task_manager.h"

namespace {

std::string Key(base::ProcessId pid, const char* category) {
  return category ?
      base::StringPrintf("process.%d.%s", pid, category) :
      base::StringPrintf("process.%d", pid);
}

using MemoryFn1 = bool (TaskManagerModel::*)(
    int index, size_t* result1) const;
using MemoryFn2 = bool (TaskManagerModel::*)(
    int index, size_t* result1, bool*) const;

int InMBFromB(size_t result_in_bytes) {
  return static_cast<int>(result_in_bytes / (1024 * 1024));
}

int InMBFromB(const TaskManagerModel* model, MemoryFn1 mfn, int index) {
  size_t result_in_bytes = 0;
  bool res = (model->*mfn)(index, &result_in_bytes);
  return res ? InMBFromB(result_in_bytes) : 0;
}

int InMBFromB(const TaskManagerModel* model, MemoryFn2 mfn, int index) {
  size_t result_in_bytes = 0;
  bool ignored;
  bool res = (model->*mfn)(index, &result_in_bytes, &ignored);
  return res ? InMBFromB(result_in_bytes) : 0;
}

class TaskManagerDataDumper :
    public base::RefCountedThreadSafe<TaskManagerDataDumper> {
 public:
  TaskManagerDataDumper(scoped_refptr<TaskManagerModel> model, base::File file)
      : model_(model), file_(std::move(file)) {
    model_->RegisterOnDataReadyCallback(
        base::Bind(&TaskManagerDataDumper::OnDataReady, this));
    model->StartListening();
    // Note that GenerateStateJSON 'new's this object which is reference
    // counted.
    AddRef();
  }

 private:
  friend class base::RefCountedThreadSafe<TaskManagerDataDumper>;
  ~TaskManagerDataDumper() {
  }

  void OnDataReady() {
    // Some data (for example V8 memory) has not yet arrived, so we wait.
    // TODO(cpu): Figure out how to make this reliable.
    static base::TimeDelta delay = base::TimeDelta::FromMilliseconds(250);
    base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
        FROM_HERE, base::Bind(&TaskManagerDataDumper::OnDataReadyDelayed, this),
        delay);
  }

  void OnDataReadyDelayed() {
    model_->StopListening();
    // Task manager finally computed (most) values. Lets generate the JSON
    // data and send it over the pipe.
    base::DictionaryValue dict;
    GatherComputerValues(&dict);
    GatherChromeValues(&dict);

    std::string json;
    auto options = base::JSONWriter::OPTIONS_PRETTY_PRINT;
    if (!base::JSONWriter::WriteWithOptions(dict, options, &json))
      return;

    file_.WriteAtCurrentPos(json.c_str(), json.size());
    file_.Close();
    // this Release() causes our destruction.
    Release();
  }

 private:
  // TODO(cpu): split the key names below into a separate header that both
  // caps and chrome can use.

  void GatherComputerValues(base::DictionaryValue* dict) {
    base::CPU cpu;
    dict->SetInteger("system.cpu.type", cpu.type());
    dict->SetInteger("system.cpu.family", cpu.family());
    dict->SetInteger("system.cpu.model", cpu.model());
    dict->SetInteger("system.cpu.stepping", cpu.stepping());
    dict->SetString("system.cpu.brand", cpu.cpu_brand());
    dict->SetInteger("system.cpu.logicalprocessors",
                     base::SysInfo::NumberOfProcessors());
    dict->SetInteger("system.cpu.logicalprocessors",
                     base::SysInfo::NumberOfProcessors());
    int64_t memory = base::SysInfo::AmountOfPhysicalMemory();
    dict->SetInteger("system.memory.physical", InMBFromB(memory));
    memory = base::SysInfo::AmountOfAvailablePhysicalMemory();
    dict->SetInteger("system.memory.available", InMBFromB(memory));
    dict->SetInteger("system.uptime", base::SysInfo::Uptime().InSeconds());
    dict->SetString("os.name", base::SysInfo::OperatingSystemName());
#if !defined(OS_LINUX)
    int32_t major, minor, bugfix;
    base::SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix);
    dict->SetInteger("os.version.major", major);
    dict->SetInteger("os.version.minor", minor);
    dict->SetInteger("os.version.bugfix", bugfix);
    dict->SetString("os.arch", base::SysInfo::OperatingSystemArchitecture());
#endif
  }

  void GatherChromeValues(base::DictionaryValue* dict) {
    for (int index = 0; index != model_->ResourceCount(); ++index) {
      auto pid = model_->GetProcessId(index);
      auto tabs_key =  Key(pid, "tabs");
      base::ListValue* tabs;
      if (!dict->GetList(tabs_key, &tabs)) {
        tabs = new base::ListValue;
        dict->Set(tabs_key, tabs);
        tabs->AppendString(model_->GetResourceTitle(index));

        dict->SetInteger(Key(pid, "memory.physical"),
            InMBFromB(model_.get(),
                      &TaskManagerModel::GetPhysicalMemory, index));
        dict->SetInteger(Key(pid, "memory.private"),
            InMBFromB(model_.get(),
                      &TaskManagerModel::GetPrivateMemory, index));
        dict->SetInteger(Key(pid, "memory.shared"),
            InMBFromB(model_.get(),
                      &TaskManagerModel::GetSharedMemory, index));
        dict->SetInteger(Key(pid, "memory.video"),
            InMBFromB(model_.get(),
                      &TaskManagerModel::GetVideoMemory, index));
        dict->SetInteger(Key(pid, "memory.V8.total"),
            InMBFromB(model_.get(),
                      &TaskManagerModel::GetV8Memory, index));
        dict->SetInteger(Key(pid, "memory.V8.used"),
            InMBFromB(model_.get(),
                      &TaskManagerModel::GetV8MemoryUsed, index));
        dict->SetInteger(Key(pid, "memory.sqlite"),
            InMBFromB(model_.get(),
                      &TaskManagerModel::GetSqliteMemoryUsedBytes, index));
        dict->SetString(Key(pid, "uptime"),
            ProcessUptime(model_->GetProcess(index)));
      } else {
        // TODO(cpu): Probably best to write the MD5 hash of the title.
        tabs->AppendString(model_->GetResourceTitle(index));
      }
    }
  }

  std::string ProcessUptime(base::ProcessHandle process) {
#if defined(OS_WIN)
    FILETIME creation_time;
    FILETIME exit_time;
    FILETIME kernel_time;
    FILETIME user_time;

    if (!GetProcessTimes(process, &creation_time, &exit_time,
                         &kernel_time, &user_time))
      return std::string("~");

    auto ct_delta = base::Time::Now() - base::Time::FromFileTime(creation_time);
    return base::StringPrintf("%lld", ct_delta.InSeconds());
#else
    return std::string();
#endif
  }

  scoped_refptr<TaskManagerModel> model_;
  base::File file_;
};

}  // namespace

namespace caps {

void GenerateStateJSON(
    scoped_refptr<TaskManagerModel> model, base::File file) {
  new TaskManagerDataDumper(model, std::move(file));
}

}