Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(276)

Side by Side Diff: components/browser_watcher/system_session_analyzer_win.cc

Issue 2715903003: Bound the impact of system instability on chrome instability. (Closed)
Patch Set: Don't inline complex destructors Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2017 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 #include "components/browser_watcher/system_session_analyzer_win.h"
6
7 #include <windows.h>
8 #include <winevt.h>
9
10 #include <utility>
11
12 #include "base/time/time.h"
13
14 namespace browser_watcher {
15
16 namespace {
17
18 // The name of the log channel to query.
19 const wchar_t kChannelName[] = L"System";
20
21 const uint16_t kIdSessionStart = 6005U;
Sigurður Ásgeirsson 2017/03/01 15:09:27 do these come out of documentation somewhere? Mayb
manzagop (departed) 2017/03/01 21:07:45 Done.
22 const uint16_t kIdSessionEnd = 6006U;
23 const uint16_t kIdSessionEndUnclean = 6008U;
24
25 // An XPATH expression to query for events marking system start or shutdown. The
26 // query looks at the following events:
27 // - id 6005: "The Event log service was started."
28 // - id 6006: "The Event log service was stopped."
29 // - id 6008: "The previous system shutdown at <time> on <date> was
30 // unexpected."
31 // This query is expected to retrieve exactly one event for each startup (id
32 // 6005) and one event for each shutdown (either 6006 or 6008).
33 const wchar_t kSessionEventsQuery[] =
34 L"*[System[Provider[@Name='eventlog']"
35 L" and (EventID=6005 or EventID=6006 or EventID=6008)]]";
36
37 // XPath expressions to attributes of interest.
38 const wchar_t kEventIdPath[] = L"Event/System/EventID";
39 const wchar_t kEventTimePath[] = L"Event/System/TimeCreated/@SystemTime";
40
41 // The timeout to use for calls to ::EvtNext.
42 const uint32_t kTimeoutMs = 5000;
43
44 struct EvtHandleCloser {
45 using pointer = EVT_HANDLE;
46 void operator()(EVT_HANDLE handle) const {
47 if (handle)
48 ::EvtClose(handle);
49 }
50 };
51
52 using EvtHandle = std::unique_ptr<EVT_HANDLE, EvtHandleCloser>;
53
54 // Create a render context (i.e. specify attributes of interest).
55 EvtHandle CreateRenderContext() {
56 LPCWSTR value_paths[] = {kEventIdPath, kEventTimePath};
57 DWORD value_cnt = 2U;
58
59 EVT_HANDLE context = NULL;
60 context =
61 ::EvtCreateRenderContext(value_cnt, value_paths, EvtRenderContextValues);
62 if (!context)
63 PLOG(ERROR) << "Failed to create render context.";
Sigurður Ásgeirsson 2017/03/01 15:09:26 DLOG, here and below. Also, the PLOG macros are u
manzagop (departed) 2017/03/01 21:07:45 Done.
64
65 return EvtHandle(context);
66 }
67
68 bool GetEventInfo(EVT_HANDLE context,
69 EVT_HANDLE event,
70 SystemSessionEventFetcher::EventInfo* info) {
71 DCHECK(context);
72 DCHECK(event);
73 DCHECK(info);
74
75 DWORD attribut_cnt = 2U;
Sigurður Ásgeirsson 2017/03/01 15:09:27 nit: attribut[e]_
manzagop (departed) 2017/03/01 21:07:45 Done.
76 std::vector<EVT_VARIANT> buffer(attribut_cnt);
Sigurður Ásgeirsson 2017/03/01 15:09:27 running commentary through here would be nice, as
manzagop (departed) 2017/03/01 21:07:45 Hopefully better. Done.
77 DWORD buffer_size = attribut_cnt * sizeof(EVT_VARIANT);
78 DWORD buffer_used = 0U;
79 DWORD value_cnt = 0U;
80
81 if (!::EvtRender(context, event, EvtRenderEventValues, buffer_size,
82 buffer.data(), &buffer_used, &value_cnt)) {
83 PLOG(ERROR) << "Failed to render the event.";
84 return false;
85 }
86
87 if ((buffer[0].Type != EvtVarTypeUInt16) ||
88 (buffer[1].Type != EvtVarTypeFileTime)) {
89 // The types are not what we expect.
90 return false;
91 }
92
93 info->event_id = buffer[0].UInt16Val;
94
95 ULONGLONG time = buffer[1].FileTimeVal;
Sigurður Ásgeirsson 2017/03/01 15:09:27 ULARGE_INTEGER has a union to crack this?
manzagop (departed) 2017/03/01 21:07:45 Done.
96 FILETIME ft;
97 ft.dwLowDateTime = static_cast<DWORD>(time & 0xFFFFFFFF);
98
99 ft.dwHighDateTime = static_cast<DWORD>(time >> 32);
100 info->event_time = base::Time::FromFileTime(ft);
101
102 return true;
103 }
104
105 } // namespace
106
107 SystemSessionEventFetcher::SystemSessionEventFetcher(uint32_t session_cnt)
108 : session_cnt_(session_cnt) {}
109
110 bool SystemSessionEventFetcher::FetchEvents(
111 std::vector<EventInfo>* event_infos) const {
112 DCHECK(event_infos);
113
114 // Query for the events. Note: requesting events from newest to oldest.
115 EVT_HANDLE query_raw =
116 ::EvtQuery(nullptr, kChannelName, kSessionEventsQuery,
117 EvtQueryChannelPath | EvtQueryReverseDirection);
118 if (!query_raw) {
119 PLOG(ERROR) << "Event query failed.";
120 return false;
121 }
122 EvtHandle query(query_raw);
123
124 // Retrieve events: 2 events per session, plus the current session's start.
125 DWORD desired_event_cnt = 2U * session_cnt_ + 1U;
126 std::vector<EVT_HANDLE> events_raw(desired_event_cnt, NULL);
127 DWORD event_cnt = 0U;
128 BOOL success = ::EvtNext(query.get(), desired_event_cnt, events_raw.data(),
129 kTimeoutMs, 0, &event_cnt);
130 if (!success) {
131 // Ensure we capture the last error immediately. Note: returning below to
132 // ensure potential handle cleanup.
133 PLOG(ERROR) << "Failed to retrieve events.";
134 }
135
136 // Ensure handles get closed. The MSDN sample seems to imply handles may need
137 // to be closed event in if EvtNext failed.
138 std::vector<EvtHandle> events(desired_event_cnt);
139 for (size_t i = 0; i < event_cnt; ++i)
140 events[i].reset(events_raw[i]);
141
142 if (!success)
143 return false;
144
145 // Extract information from the events.
146 EvtHandle render_context = CreateRenderContext();
147 if (!render_context.get())
148 return false;
149
150 std::vector<EventInfo> event_infos_tmp;
151 event_infos_tmp.reserve(event_cnt);
152
153 EventInfo info = {};
154 for (size_t i = 0; i < event_cnt; ++i) {
155 if (!GetEventInfo(render_context.get(), events[i].get(), &info))
156 return false;
157 event_infos_tmp.push_back(info);
158 }
159
160 event_infos->swap(event_infos_tmp);
161 return true;
162 }
163
164 SystemSessionAnalyzer::SystemSessionAnalyzer(
165 std::unique_ptr<SystemSessionEventFetcher> fetcher)
166 : event_fetcher_(std::move(fetcher)) {}
167
168 SystemSessionAnalyzer::~SystemSessionAnalyzer() {}
169
170 SystemSessionAnalyzer::Status SystemSessionAnalyzer::IsSessionUnclean(
171 base::Time timestamp) {
172 if (!initialized_)
173 Initialize();
174 if (!init_success_)
175 return FAILED;
176 if (timestamp < coverage_start_)
177 return OUTSIDE_RANGE;
178
179 // Get the first session starting after the timestamp.
180 std::map<base::Time, base::TimeDelta>::const_iterator it =
181 unclean_sessions_.upper_bound(timestamp);
182 if (it == unclean_sessions_.begin())
183 return CLEAN; // No prior unclean session.
184
185 // Get the previous session and see if it encompasses the timestamp.
186 --it;
187 bool is_spanned = (timestamp - it->first) <= it->second;
188 return is_spanned ? UNCLEAN : CLEAN;
189 }
190
191 void SystemSessionAnalyzer::Initialize() {
192 initialized_ = true;
193
194 if (!event_fetcher_)
195 return;
196 std::vector<SystemSessionEventFetcher::EventInfo> events;
197 if (!event_fetcher_->FetchEvents(&events))
198 return;
199
200 // Validate the number and ordering of events (newest to oldest).
201 size_t event_cnt = events.size();
202 if ((event_cnt % 2) == 0)
203 return; // Even number is unexpected.
204 if (event_cnt < 3)
205 return; // Not enough data for a single session.
206 for (size_t i = 1; i < event_cnt; ++i) {
207 if (events[i - 1].event_time < events[i].event_time)
208 return;
209 }
210
211 std::map<base::Time, base::TimeDelta> unclean_sessions;
212 size_t start = 2U;
213 size_t end = 1U;
214 for (; start < event_cnt && end < event_cnt; start += 2, end += 2) {
215 uint16_t start_id = events[start].event_id;
216 uint16_t end_id = events[end].event_id;
217 if (start_id != kIdSessionStart)
218 return;
219 if (end_id != kIdSessionEnd && end_id != kIdSessionEndUnclean)
220 return;
221
222 if (end_id == kIdSessionEnd)
223 continue;
224
225 unclean_sessions.insert(
226 std::make_pair(events[start].event_time,
227 events[end].event_time - events[start].event_time));
228 }
229
230 unclean_sessions_.swap(unclean_sessions);
231 coverage_start_ = events[event_cnt - 1].event_time;
232 init_success_ = true;
233 }
234
235 } // namespace browser_watcher
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698