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

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: Address Siggi's comments 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 // Event ids of system startup / shutdown events. These were obtained from
22 // inspection of the System log in Event Viewer on Windows 10:
23 // - id 6005: "The Event log service was started."
24 // - id 6006: "The Event log service was stopped."
25 // - id 6008: "The previous system shutdown at <time> on <date> was
26 // unexpected."
27 const uint16_t kIdSessionStart = 6005U;
28 const uint16_t kIdSessionEnd = 6006U;
29 const uint16_t kIdSessionEndUnclean = 6008U;
30
31 // An XPATH expression to query for system startup / shutdown events. The query
32 // is expected to retrieve exactly one event for each startup (kIdSessionStart)
33 // and one event for each shutdown (either kIdSessionEnd or
34 // kIdSessionEndUnclean).
35 const wchar_t kSessionEventsQuery[] =
36 L"*[System[Provider[@Name='eventlog']"
37 L" and (EventID=6005 or EventID=6006 or EventID=6008)]]";
38
39 // XPath expressions to attributes of interest.
40 const wchar_t kEventIdPath[] = L"Event/System/EventID";
41 const wchar_t kEventTimePath[] = L"Event/System/TimeCreated/@SystemTime";
42
43 // The timeout to use for calls to ::EvtNext.
44 const uint32_t kTimeoutMs = 5000;
45
46 struct EvtHandleCloser {
47 using pointer = EVT_HANDLE;
48 void operator()(EVT_HANDLE handle) const {
49 if (handle)
50 ::EvtClose(handle);
51 }
52 };
53
54 using EvtHandle = std::unique_ptr<EVT_HANDLE, EvtHandleCloser>;
55
56 base::Time ULLFileTimeToTime(ULONGLONG time_ulonglong) {
57 // Copy low / high parts as FILETIME is not always 64bit aligned.
58 ULARGE_INTEGER time;
59 time.QuadPart = time_ulonglong;
60 FILETIME ft;
61 ft.dwLowDateTime = time.LowPart;
62 ft.dwHighDateTime = time.HighPart;
63
64 return base::Time::FromFileTime(ft);
65 }
66
67 // Create a render context (i.e. specify attributes of interest).
68 EvtHandle CreateRenderContext() {
69 LPCWSTR value_paths[] = {kEventIdPath, kEventTimePath};
70 DWORD value_cnt = 2U;
Sigurður Ásgeirsson 2017/03/06 19:00:10 nit: kValueCnt, maybe initialized from arraysize()
manzagop (departed) 2017/03/06 21:14:42 Done.
71
72 EVT_HANDLE context = NULL;
73 context =
74 ::EvtCreateRenderContext(value_cnt, value_paths, EvtRenderContextValues);
75 if (!context)
76 DLOG(ERROR) << "Failed to create render context.";
77
78 return EvtHandle(context);
79 }
80
81 bool GetEventInfo(EVT_HANDLE context,
82 EVT_HANDLE event,
83 SystemSessionEventFetcher::EventInfo* info) {
84 DCHECK(context);
85 DCHECK(event);
86 DCHECK(info);
87
88 // Retrieve attributes of interest from the event. We expect the context to
89 // specify the retrieval two attributes (event id and event time), each with
Sigurður Ásgeirsson 2017/03/06 19:00:10 nit: retrieval of?
manzagop (departed) 2017/03/06 21:14:41 Done.
90 // a specific type.
91 const DWORD kAttributeCnt = 2U;
92 std::vector<EVT_VARIANT> buffer(kAttributeCnt);
93 DWORD buffer_size = kAttributeCnt * sizeof(EVT_VARIANT);
94 DWORD buffer_used = 0U;
95 DWORD retrieved_attribute_cnt = 0U;
96 if (!::EvtRender(context, event, EvtRenderEventValues, buffer_size,
97 buffer.data(), &buffer_used, &retrieved_attribute_cnt)) {
98 DLOG(ERROR) << "Failed to render the event.";
99 return false;
100 }
101
102 // Validate the count and types of the retrieved attributes.
103 if ((retrieved_attribute_cnt != kAttributeCnt) ||
104 (buffer[0].Type != EvtVarTypeUInt16) ||
105 (buffer[1].Type != EvtVarTypeFileTime)) {
106 return false;
107 }
108
109 info->event_id = buffer[0].UInt16Val;
110 info->event_time = ULLFileTimeToTime(buffer[1].FileTimeVal);
111
112 return true;
113 }
114
115 } // namespace
116
117 SystemSessionEventFetcher::SystemSessionEventFetcher(uint32_t session_cnt)
118 : session_cnt_(session_cnt) {}
119
120 bool SystemSessionEventFetcher::FetchEvents(
121 std::vector<EventInfo>* event_infos) const {
122 DCHECK(event_infos);
123
124 // Query for the events. Note: requesting events from newest to oldest.
125 EVT_HANDLE query_raw =
126 ::EvtQuery(nullptr, kChannelName, kSessionEventsQuery,
127 EvtQueryChannelPath | EvtQueryReverseDirection);
128 if (!query_raw) {
129 DLOG(ERROR) << "Event query failed.";
130 return false;
131 }
132 EvtHandle query(query_raw);
133
134 // Retrieve events: 2 events per session, plus the current session's start.
135 DWORD desired_event_cnt = 2U * session_cnt_ + 1U;
136 std::vector<EVT_HANDLE> events_raw(desired_event_cnt, NULL);
137 DWORD event_cnt = 0U;
138 BOOL success = ::EvtNext(query.get(), desired_event_cnt, events_raw.data(),
139 kTimeoutMs, 0, &event_cnt);
140 if (!success) {
141 // Ensure we capture the last error immediately. Note: returning below to
Sigurður Ásgeirsson 2017/03/06 19:00:10 missing code for this - would have to ::GetLastErr
manzagop (departed) 2017/03/06 21:14:41 Removed, as I'm no longer using PLOG.
142 // ensure potential handle cleanup.
143 DLOG(ERROR) << "Failed to retrieve events.";
144 }
145
146 // Ensure handles get closed. The MSDN sample seems to imply handles may need
147 // to be closed event in if EvtNext failed.
148 std::vector<EvtHandle> events(desired_event_cnt);
149 for (size_t i = 0; i < event_cnt; ++i)
150 events[i].reset(events_raw[i]);
151
152 if (!success)
153 return false;
154
155 // Extract information from the events.
156 EvtHandle render_context = CreateRenderContext();
157 if (!render_context.get())
158 return false;
159
160 std::vector<EventInfo> event_infos_tmp;
161 event_infos_tmp.reserve(event_cnt);
162
163 EventInfo info = {};
164 for (size_t i = 0; i < event_cnt; ++i) {
165 if (!GetEventInfo(render_context.get(), events[i].get(), &info))
166 return false;
167 event_infos_tmp.push_back(info);
168 }
169
170 event_infos->swap(event_infos_tmp);
171 return true;
172 }
173
174 SystemSessionAnalyzer::SystemSessionAnalyzer(SystemSessionEventFetcher* fetcher)
175 : event_fetcher_(fetcher) {}
176
177 SystemSessionAnalyzer::~SystemSessionAnalyzer() {}
178
179 SystemSessionAnalyzer::Status SystemSessionAnalyzer::IsSessionUnclean(
180 base::Time timestamp) {
181 if (!initialized_)
182 Initialize();
183 if (!init_success_)
Sigurður Ásgeirsson 2017/03/06 19:00:09 you might want to return a bool from Initialize, a
manzagop (departed) 2017/03/06 21:14:41 Done.
184 return FAILED;
185 if (timestamp < coverage_start_)
186 return OUTSIDE_RANGE;
187
188 // Get the first session starting after the timestamp.
189 std::map<base::Time, base::TimeDelta>::const_iterator it =
190 unclean_sessions_.upper_bound(timestamp);
191 if (it == unclean_sessions_.begin())
192 return CLEAN; // No prior unclean session.
193
194 // Get the previous session and see if it encompasses the timestamp.
195 --it;
196 bool is_spanned = (timestamp - it->first) <= it->second;
197 return is_spanned ? UNCLEAN : CLEAN;
198 }
199
200 void SystemSessionAnalyzer::Initialize() {
201 initialized_ = true;
202
203 if (!event_fetcher_)
204 return;
205 std::vector<SystemSessionEventFetcher::EventInfo> events;
206 if (!event_fetcher_->FetchEvents(&events))
207 return;
208
209 // Validate the number and ordering of events (newest to oldest).
210 size_t event_cnt = events.size();
211 if ((event_cnt % 2) == 0)
212 return; // Even number is unexpected.
213 if (event_cnt < 3)
Sigurður Ásgeirsson 2017/03/06 19:00:10 don't we get stopped/started pairs for normal shut
manzagop (departed) 2017/03/06 21:14:41 Done.
214 return; // Not enough data for a single session.
215 for (size_t i = 1; i < event_cnt; ++i) {
216 if (events[i - 1].event_time < events[i].event_time)
217 return;
218 }
219
220 std::map<base::Time, base::TimeDelta> unclean_sessions;
221 size_t start = 2U;
222 size_t end = 1U;
223 for (; start < event_cnt && end < event_cnt; start += 2, end += 2) {
Sigurður Ásgeirsson 2017/03/06 19:00:09 this loop needs some running commentary, please.
manzagop (departed) 2017/03/06 21:14:41 Done.
224 uint16_t start_id = events[start].event_id;
225 uint16_t end_id = events[end].event_id;
226 if (start_id != kIdSessionStart)
227 return;
228 if (end_id != kIdSessionEnd && end_id != kIdSessionEndUnclean)
229 return;
230
231 if (end_id == kIdSessionEnd)
232 continue;
233
234 unclean_sessions.insert(
235 std::make_pair(events[start].event_time,
236 events[end].event_time - events[start].event_time));
237 }
238
239 unclean_sessions_.swap(unclean_sessions);
240 coverage_start_ = events[event_cnt - 1].event_time;
241 init_success_ = true;
242 }
243
244 } // namespace browser_watcher
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698