1 : // Copyright 2014 Google Inc. All Rights Reserved.
2 : //
3 : // Licensed under the Apache License, Version 2.0 (the "License");
4 : // you may not use this file except in compliance with the License.
5 : // You may obtain a copy of the License at
6 : //
7 : // http://www.apache.org/licenses/LICENSE-2.0
8 : //
9 : // Unless required by applicable law or agreed to in writing, software
10 : // distributed under the License is distributed on an "AS IS" BASIS,
11 : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 : // See the License for the specific language governing permissions and
13 : // limitations under the License.
14 :
15 : #include "syzygy/agent/memprof/memory_profiler.h"
16 :
17 : #include "base/bind.h"
18 : #include "syzygy/agent/common/process_utils.h"
19 : #include "syzygy/common/process_utils.h"
20 :
21 m : namespace agent {
22 m : namespace memprof {
23 :
24 m : MemoryProfiler::MemoryProfiler()
25 m : : function_call_logger_(&session_) {
26 m : SetDefaultParameters(¶meters_);
27 m : }
28 :
29 m : bool MemoryProfiler::Init() {
30 : // We don't care if parameter parsing fails at runtime; such parameters will
31 : // simply be ignored.
32 m : ParseParametersFromEnv(¶meters_);
33 m : PropagateParameters();
34 m : ThreadState* state = GetOrAllocateThreadState();
35 m : if (!trace::client::InitializeRpcSession(
36 m : &session_, state->segment())) {
37 m : return false;
38 m : }
39 :
40 : // Get a list of all existing heaps.
41 m : std::vector<HANDLE> heaps(16, 0);
42 m : size_t heap_count = GetProcessHeaps(0, nullptr);
43 m : heaps.resize(heap_count);
44 m : heap_count = GetProcessHeaps(heaps.size(), heaps.data());
45 m : DCHECK_EQ(heap_count, heaps.size());
46 :
47 : // Ensure the process heap is reported first.
48 m : HANDLE proc_heap = ::GetProcessHeap();
49 m : for (size_t i = 1; i < heaps.size(); ++i) {
50 m : if (heaps[i] == proc_heap) {
51 m : heaps[i] = heaps[0];
52 m : heaps[0] = proc_heap;
53 m : break;
54 m : }
55 m : }
56 :
57 : // Log all pre-existing heaps.
58 m : for (auto heap : heaps) {
59 m : CHECK(state->segment()->CanAllocate(sizeof(TraceProcessHeap)) ||
60 m : session_.ExchangeBuffer(state->segment()));
61 m : DCHECK(state->segment()->CanAllocate(sizeof(TraceProcessHeap)));
62 m : TraceProcessHeap* proc_heap =
63 m : state->segment()->AllocateTraceRecord<TraceProcessHeap>();
64 m : DCHECK(proc_heap);
65 m : static_assert(sizeof(proc_heap->process_heap) == sizeof(heap),
66 m : "incompatible sizes of heap handle types");
67 m : proc_heap->process_heap = reinterpret_cast<uint32_t>(heap);
68 m : }
69 :
70 : // Setup the DLL watcher. This will be notified of module load and unload
71 : // events as they occur.
72 m : dll_watcher_.Init(base::Bind(&MemoryProfiler::OnDllEvent,
73 m : base::Unretained(this)));
74 :
75 : // Log all modules that are already loaded when we are. Further modules
76 : // will be logged as they load and unload via the DllNotification
77 : // mechanism.
78 m : LogAllModules();
79 :
80 m : return true;
81 m : }
82 :
83 m : MemoryProfiler::ThreadState* MemoryProfiler::GetOrAllocateThreadState() {
84 m : ThreadState* data = GetOrAllocateThreadStateImpl();
85 m : if (!data->segment()->write_ptr && session_.IsTracing())
86 m : session_.AllocateBuffer(data->segment());
87 :
88 m : return data;
89 m : }
90 :
91 m : MemoryProfiler::ThreadState* MemoryProfiler::GetThreadState() {
92 m : return tls_.Get();
93 m : }
94 :
95 m : void MemoryProfiler::PropagateParameters() {
96 m : function_call_logger_.set_stack_trace_tracking(
97 m : parameters_.stack_trace_tracking);
98 m : function_call_logger_.set_serialize_timestamps(
99 m : parameters_.serialize_timestamps);
100 m : }
101 :
102 m : MemoryProfiler::ThreadState* MemoryProfiler::GetOrAllocateThreadStateImpl() {
103 m : ThreadState *data = tls_.Get();
104 m : if (data != NULL)
105 m : return data;
106 :
107 m : data = new ThreadState(this);
108 m : if (data == NULL) {
109 m : LOG(ERROR) << "Unable to allocate per-thread data";
110 m : return NULL;
111 m : }
112 :
113 m : thread_state_manager_.Register(data);
114 m : tls_.Set(data);
115 :
116 m : return data;
117 m : }
118 :
119 m : void MemoryProfiler::LogAllModules() {
120 m : ::common::ModuleVector modules;
121 m : ::common::GetCurrentProcessModules(&modules);
122 :
123 m : for (size_t i = 0; i < modules.size(); ++i) {
124 m : DCHECK(modules[i] != NULL);
125 m : LogModule(modules[i]);
126 m : }
127 :
128 : // We need to flush module events right away, so that the module is
129 : // defined in the trace file before events using that module start to
130 : // occur.
131 m : GetOrAllocateThreadState()->FlushSegment();
132 m : }
133 :
134 m : void MemoryProfiler::LogModule(HMODULE module) {
135 m : {
136 m : base::AutoLock lock(lock_);
137 m : bool inserted = logged_modules_.insert(module).second;
138 m : if (!inserted)
139 m : return;
140 m : }
141 :
142 m : ThreadState* state = GetOrAllocateThreadState();
143 m : agent::common::LogModule(module, &session_, state->segment());
144 m : }
145 :
146 m : void MemoryProfiler::OnDllEvent(
147 m : agent::common::DllNotificationWatcher::EventType type,
148 m : HMODULE module,
149 m : size_t module_size,
150 m : const base::StringPiece16& dll_path,
151 m : const base::StringPiece16& dll_base_name) {
152 m : switch (type) {
153 m : case agent::common::DllNotificationWatcher::kDllLoaded: {
154 m : LogModule(module);
155 m : break;
156 m : }
157 :
158 m : case agent::common::DllNotificationWatcher::kDllUnloaded: {
159 m : base::AutoLock lock(lock_);
160 m : logged_modules_.erase(module);
161 m : break;
162 m : }
163 m : }
164 :
165 m : return;
166 m : }
167 :
168 m : MemoryProfiler::ThreadState::ThreadState(MemoryProfiler* parent)
169 m : : parent_(parent) {
170 m : DCHECK_NE(static_cast<MemoryProfiler*>(nullptr), parent);
171 m : }
172 :
173 m : bool MemoryProfiler::ThreadState::FlushSegment() {
174 m : if (!parent_->session_.ExchangeBuffer(&segment_))
175 m : return false;
176 m : return true;
177 m : }
178 :
179 m : } // namespace memprof
180 m : } // namespace agent
|