OLD | NEW |
1 #!/bin/sh | 1 #!/bin/sh |
2 | 2 |
3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. | 3 # Copyright (c) 2010 The Chromium OS Authors. All rights reserved. |
4 # Use of this source code is governed by a BSD-style license that can be | 4 # Use of this source code is governed by a BSD-style license that can be |
5 # found in the LICENSE file. | 5 # found in the LICENSE file. |
6 | 6 |
7 set -e | 7 set -e |
8 | 8 |
9 # Product ID in crash report | 9 # Product ID in crash report |
10 CHROMEOS_PRODUCT=ChromeOS | 10 CHROMEOS_PRODUCT=ChromeOS |
11 | 11 |
12 # Should remove the run file when this process finishes. We don't want | 12 # Should remove the run file when this process finishes. We don't want |
13 # to always remove it since it may be for pre-existing crash_sender | 13 # to always remove it since it may be for pre-existing crash_sender |
14 # process. | 14 # process. |
15 CLEAN_UP_RUN_FILE=0 | 15 CLEAN_UP_RUN_FILE=0 |
16 | 16 |
17 # File whose existence implies crash reports may be sent, and whose | 17 # File whose existence implies crash reports may be sent, and whose |
18 # contents includes our machine's anonymized guid. | 18 # contents includes our machine's anonymized guid. |
19 CONSENT_ID="/home/chronos/Consent To Send Stats" | 19 CONSENT_ID="/home/chronos/Consent To Send Stats" |
20 | 20 |
21 # Path to find which is required for computing the crash rate. | 21 # Path to find which is required for computing the crash rate. |
22 FIND="/usr/bin/find" | 22 FIND="/usr/bin/find" |
23 | 23 |
| 24 # Set this to 1 in the environment to allow uploading crash reports |
| 25 # for unofficial versions. |
| 26 FORCE_OFFICIAL=${FORCE_OFFICIAL:-0} |
| 27 |
| 28 # Path to hardware class description. |
| 29 HWCLASS_PATH="/sys/devices/platform/chromeos_acpi/HWID" |
| 30 |
24 # Maximum crashes to send per day. | 31 # Maximum crashes to send per day. |
25 MAX_CRASH_RATE=32 | 32 MAX_CRASH_RATE=${MAX_CRASH_RATE:-32} |
26 | 33 |
27 # File whose existence mocks crash sending. If empty we pretend the | 34 # File whose existence mocks crash sending. If empty we pretend the |
28 # crash sending was successful, otherwise unsuccessful. | 35 # crash sending was successful, otherwise unsuccessful. |
29 MOCK_CRASH_SENDING="/tmp/mock-crash-sending" | 36 MOCK_CRASH_SENDING="/tmp/mock-crash-sending" |
30 | 37 |
31 # File whose existence causes crash sending to be delayed (for testing). | 38 # File whose existence causes crash sending to be delayed (for testing). |
32 # Must be stateful to enable testing kernel crashes. | 39 # Must be stateful to enable testing kernel crashes. |
33 PAUSE_CRASH_SENDING="/var/lib/crash_sender_paused" | 40 PAUSE_CRASH_SENDING="/var/lib/crash_sender_paused" |
34 | 41 |
35 # URL to send non-official build crash reports to. | |
36 REPORT_UPLOAD_STAGING_URL="http://clients2.google.com/cr/staging_report" | |
37 | |
38 # URL to send official build crash reports to. | 42 # URL to send official build crash reports to. |
39 REPORT_UPLOAD_PROD_URL="http://clients2.google.com/cr/report" | 43 REPORT_UPLOAD_PROD_URL="http://clients2.google.com/cr/report" |
40 | 44 |
41 # File whose existence implies we're running and not to start again. | 45 # File whose existence implies we're running and not to start again. |
42 RUN_FILE="/var/run/crash_sender.pid" | 46 RUN_FILE="/var/run/crash_sender.pid" |
43 | 47 |
44 # Maximum time to sleep between sends. | 48 # Maximum time to sleep between sends. |
45 SECONDS_SEND_SPREAD=${SECONDS_SEND_SPREAD:-600} | 49 SECONDS_SEND_SPREAD=${SECONDS_SEND_SPREAD:-600} |
46 | 50 |
47 # The syslog tag for all logging we emit. | 51 # The syslog tag for all logging we emit. |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
82 # Note that this write may be executed by two crash_senders who | 86 # Note that this write may be executed by two crash_senders who |
83 # simulataneously reap the existing dangling run file | 87 # simulataneously reap the existing dangling run file |
84 echo $$ > "${RUN_FILE}" | 88 echo $$ > "${RUN_FILE}" |
85 return | 89 return |
86 fi | 90 fi |
87 # This could just be an unrelated process, but it's ok to be conservative. | 91 # This could just be an unrelated process, but it's ok to be conservative. |
88 lecho "Already running. Exiting now." | 92 lecho "Already running. Exiting now." |
89 exit 1 | 93 exit 1 |
90 } | 94 } |
91 | 95 |
92 get_version() { | |
93 grep ^CHROMEOS_RELEASE_VERSION /etc/lsb-release | cut -d = -f 2- | |
94 } | |
95 | |
96 is_official() { | 96 is_official() { |
| 97 [ ${FORCE_OFFICIAL} -ne 0 ] && return 0 |
97 grep ^CHROMEOS_RELEASE_DESCRIPTION /etc/lsb-release | grep -q Official | 98 grep ^CHROMEOS_RELEASE_DESCRIPTION /etc/lsb-release | grep -q Official |
98 } | 99 } |
99 | 100 |
100 # Generate a uniform random number in 0..max-1. | 101 # Generate a uniform random number in 0..max-1. |
101 generate_uniform_random() { | 102 generate_uniform_random() { |
102 local max=$1 | 103 local max=$1 |
103 local random="$(od -An -N4 -tu /dev/urandom)" | 104 local random="$(od -An -N4 -tu /dev/urandom)" |
104 echo $((random % max)) | 105 echo $((random % max)) |
105 } | 106 } |
106 | 107 |
107 is_feedback_disabled() { | 108 is_feedback_disabled() { |
108 [ -r "${CONSENT_ID}" ] && return 1 | 109 [ -r "${CONSENT_ID}" ] && return 1 |
109 return 0 | 110 return 0 |
110 } | 111 } |
111 | 112 |
112 is_on_3g() { | 113 is_on_3g() { |
113 # See crosbug.com/3304. | 114 # See crosbug.com/3304. |
114 return 1 | 115 return 1 |
115 } | 116 } |
116 | 117 |
117 # Check if sending a crash now does not exceed the maximum 24hr rate and | 118 # Check if sending a crash now does not exceed the maximum 24hr rate and |
118 # commit to doing so, if not. | 119 # commit to doing so, if not. |
119 check_rate() { | 120 check_rate() { |
120 mkdir -p ${TIMESTAMPS_DIR} | 121 mkdir -p ${TIMESTAMPS_DIR} |
121 # Only consider minidumps written in the past 24 hours by removing all older. | 122 # Only consider minidumps written in the past 24 hours by removing all older. |
122 ${FIND} "${TIMESTAMPS_DIR}" -mindepth 1 -mmin +$((24 * 60)) -exec rm '{}' ';' | 123 ${FIND} "${TIMESTAMPS_DIR}" -mindepth 1 -mmin +$((24 * 60)) \ |
| 124 -exec rm -- '{}' ';' |
123 local sends_in_24hrs=$(echo "${TIMESTAMPS_DIR}"/* | wc -w) | 125 local sends_in_24hrs=$(echo "${TIMESTAMPS_DIR}"/* | wc -w) |
124 lecho "Current send rate: ${sends_in_24hrs}sends/24hrs" | 126 lecho "Current send rate: ${sends_in_24hrs}sends/24hrs" |
125 if [ ${sends_in_24hrs} -ge ${MAX_CRASH_RATE} ]; then | 127 if [ ${sends_in_24hrs} -ge ${MAX_CRASH_RATE} ]; then |
126 lecho "Cannot send more crashes:" | 128 lecho "Cannot send more crashes:" |
127 lecho " current ${sends_in_24hrs}send/24hrs >= " \ | 129 lecho " current ${sends_in_24hrs}send/24hrs >= " \ |
128 "max ${MAX_CRASH_RATE}send/24hrs" | 130 "max ${MAX_CRASH_RATE}send/24hrs" |
129 return 1 | 131 return 1 |
130 fi | 132 fi |
131 mktemp "${TIMESTAMPS_DIR}"/XXXX > /dev/null | 133 mktemp "${TIMESTAMPS_DIR}"/XXXX > /dev/null |
132 return 0 | 134 return 0 |
133 } | 135 } |
134 | 136 |
135 # Return if $1 is a .core file | 137 # Gets the base part of a crash report file, such as |
136 get_kind() { | 138 # name.01234.5678.9012 from name.01234.5678.9012.meta |
137 local kind="${1##*.}" | 139 get_base() { |
138 if [ "${kind}" = "dmp" ]; then | 140 echo "${1%.*}" |
139 kind="minidump" | |
140 fi | |
141 echo ${kind} | |
142 } | 141 } |
143 | 142 |
144 get_exec_name() { | 143 # Return which kind of report the given metadata file relates to |
145 local filename=$(basename "$1") | 144 get_kind() { |
146 echo "${filename%%.*}" | 145 # There should never be a report with both a dmp and kcrash file. |
| 146 # If that were to happen we arbitrarily consider this a minidump |
| 147 # report and effectively ignore the kcrash. |
| 148 local base="$(get_base "$1")" |
| 149 if [ -r "${base}.dmp" ]; then |
| 150 echo "minidump" |
| 151 return |
| 152 fi |
| 153 if [ -r "${base}.kcrash" ]; then |
| 154 echo "kcrash" |
| 155 return |
| 156 fi |
| 157 } |
| 158 |
| 159 get_key_value() { |
| 160 if ! grep -q "$2=" "$1"; then |
| 161 echo "undefined" |
| 162 return |
| 163 fi |
| 164 grep "$2=" "$1" | cut -d = -f 2- |
| 165 } |
| 166 |
| 167 # Returns true if mock is enabled. |
| 168 is_mock() { |
| 169 [ -f "${MOCK_CRASH_SENDING}" ] && return 0 |
| 170 return 1 |
| 171 } |
| 172 |
| 173 # Return the board name. |
| 174 get_board() { |
| 175 echo $(get_key_value "/etc/lsb-release" "CHROMEOS_RELEASE_BOARD") |
| 176 } |
| 177 |
| 178 # Return the hardware class or "unknown". |
| 179 get_hardware_class() { |
| 180 if [ -r "${HWCLASS_PATH}" ]; then |
| 181 cat "${HWCLASS_PATH}" |
| 182 else |
| 183 echo "unknown" |
| 184 fi |
147 } | 185 } |
148 | 186 |
149 send_crash() { | 187 send_crash() { |
150 local report_path="$1" | 188 local meta_path="$1" |
151 local kind=$(get_kind "${report_path}") | 189 local kind="$(get_kind "${meta_path}")" |
152 local exec_name=$(get_exec_name "${report_path}") | 190 local exec_name="$(get_key_value "${meta_path}" "exec_name")" |
153 local sleep_time=$(generate_uniform_random $SECONDS_SEND_SPREAD) | 191 local sleep_time=$(generate_uniform_random $SECONDS_SEND_SPREAD) |
154 local url="${REPORT_UPLOAD_STAGING_URL}" | 192 local url="${REPORT_UPLOAD_PROD_URL}" |
155 if is_official; then | 193 local chromeos_version="$(get_key_value "${meta_path}" "ver")" |
156 url="${REPORT_UPLOAD_PROD_URL}" | 194 local board="$(get_board)" |
157 fi | 195 local hwclass="$(get_hardware_class)" |
| 196 local payload_extension="${kind}" |
| 197 [ "${kind}" = "minidump" ] && payload_extension="dmp" |
| 198 local report_payload="$(get_base "${meta_path}").${payload_extension}" |
158 lecho "Sending crash:" | 199 lecho "Sending crash:" |
159 lecho " Scheduled to send in ${sleep_time}s" | 200 lecho " Scheduled to send in ${sleep_time}s" |
160 lecho " Report: ${report_path} (${kind})" | 201 lecho " Metadata: ${meta_path} (${kind})" |
161 lecho " URL: ${url}" | 202 lecho " Payload: ${report_payload}" |
162 lecho " Product: ${CHROMEOS_PRODUCT}" | |
163 lecho " Version: ${chromeos_version}" | 203 lecho " Version: ${chromeos_version}" |
| 204 if is_mock; then |
| 205 lecho " Product: ${CHROMEOS_PRODUCT}" |
| 206 lecho " URL: ${url}" |
| 207 lecho " Board: ${board}" |
| 208 lecho " HWClass: ${hwclass}" |
| 209 fi |
164 lecho " Exec name: ${exec_name}" | 210 lecho " Exec name: ${exec_name}" |
165 if [ -f "${MOCK_CRASH_SENDING}" ]; then | 211 if is_mock; then |
166 local mock_in=$(cat "${MOCK_CRASH_SENDING}") | 212 local mock_in=$(cat "${MOCK_CRASH_SENDING}") |
167 if [ "${mock_in}" = "" ]; then | 213 if [ "${mock_in}" = "" ]; then |
168 lecho "Mocking successful send" | 214 lecho "Mocking successful send" |
169 return 0 | 215 return 0 |
170 else | 216 else |
171 lecho "Mocking unsuccessful send" | 217 lecho "Mocking unsuccessful send" |
172 return 1 | 218 return 1 |
173 fi | 219 fi |
174 fi | 220 fi |
175 | 221 |
176 if ! sleep ${sleep_time}; then | 222 if ! sleep ${sleep_time}; then |
177 lecho "Sleep failed" | 223 lecho "Sleep failed" |
178 return 1 | 224 return 1 |
179 fi | 225 fi |
180 | 226 |
181 local report_id="${TMP_DIR}/report_id" | 227 local report_id="${TMP_DIR}/report_id" |
182 local curl_stderr="${TMP_DIR}/curl_stderr" | 228 local curl_stderr="${TMP_DIR}/curl_stderr" |
183 | 229 |
184 set +e | 230 set +e |
185 curl "${url}" \ | 231 curl "${url}" \ |
186 -F "prod=${CHROMEOS_PRODUCT}" \ | 232 -F "prod=${CHROMEOS_PRODUCT}" \ |
187 -F "ver=${chromeos_version}" \ | 233 -F "ver=${chromeos_version}" \ |
188 -F "upload_file_${kind}=@${report_path}" \ | 234 -F "upload_file_${kind}=@${report_payload}" \ |
| 235 -F "board=${board}" \ |
| 236 -F "hwclass=${hwclass}" \ |
189 -F "exec_name=${exec_name}" \ | 237 -F "exec_name=${exec_name}" \ |
190 -F "guid=<${CONSENT_ID}" -o "${report_id}" 2>"${curl_stderr}" | 238 -F "guid=<${CONSENT_ID}" -o "${report_id}" 2>"${curl_stderr}" |
191 curl_result=$? | 239 curl_result=$? |
192 set -e | 240 set -e |
193 | 241 |
194 if [ ${curl_result} -eq 0 ]; then | 242 if [ ${curl_result} -eq 0 ]; then |
195 lecho "Crash report receipt ID $(cat ${report_id})" | 243 lecho "Crash report receipt ID $(cat ${report_id})" |
196 else | 244 else |
197 lecho "Crash sending failed with: $(cat ${curl_stderr})" | 245 lecho "Crash sending failed with: $(cat ${curl_stderr})" |
198 fi | 246 fi |
199 | 247 |
200 rm -f "${report_id}" | 248 rm -f "${report_id}" |
201 | 249 |
202 return ${curl_result} | 250 return ${curl_result} |
203 } | 251 } |
204 | 252 |
| 253 # *.meta files always end with done=1 so we can tell if they are complete. |
| 254 is_complete_metadata() { |
| 255 grep -q "done=1" "$1" |
| 256 } |
| 257 |
| 258 # Remove the given report path. |
| 259 remove_report() { |
| 260 local base="${1%.*}" |
| 261 rm -f -- "${base}".* |
| 262 } |
| 263 |
205 # Send all crashes from the given directory. | 264 # Send all crashes from the given directory. |
206 send_crashes() { | 265 send_crashes() { |
207 local dir="$1" | 266 local dir="$1" |
208 lecho "Considering crashes in ${dir}" | |
209 | 267 |
210 # Cycle through minidumps, most recent first. That way if we're about | 268 # Cycle through minidumps, most recent first. That way if we're about |
211 # to exceed the daily rate, we send the most recent minidumps. | 269 # to exceed the daily rate, we send the most recent minidumps. |
212 if [ ! -d "${dir}" ]; then | 270 if [ ! -d "${dir}" ]; then |
213 return | 271 return |
214 fi | 272 fi |
215 for file in $(ls -1t "${dir}"); do | |
216 local report_path="${dir}/${file}" | |
217 lecho "Considering file ${report_path}" | |
218 local kind=$(get_kind "${report_path}") | |
219 | 273 |
220 if [ "${kind}" = "core" ]; then | 274 # Consider any old files which still have no corresponding meta file |
221 lecho "Ignoring core file." | 275 # as orphaned, and remove them. |
222 continue | 276 for old_file in $(${FIND} "${dir}" -mindepth 1 \ |
223 elif [ "${kind}" != "minidump" ] && [ "${kind}" != "kcrash" ]; then | 277 -mmin +$((24 * 60)) -type f); do |
224 lecho "Unknown report kind: ${kind}. Removing report." | 278 if [ ! -e "$(get_base "${old_file}").meta" ]; then |
225 rm -f "${report_path}" | 279 lecho "Removing old orphaned file: ${old_file}." |
| 280 rm -f -- "${old_file}" |
| 281 fi |
| 282 done |
| 283 |
| 284 # Look through all metadata (*.meta) files, if any exist. |
| 285 for meta_path in $(ls -1t "${dir}"/*.meta 2>/dev/null); do |
| 286 lecho "Considering metadata ${meta_path}." |
| 287 local kind=$(get_kind "${meta_path}") |
| 288 |
| 289 if [ "${kind}" != "minidump" ] && [ "${kind}" != "kcrash" ]; then |
| 290 lecho "Unknown report kind. Removing report." |
| 291 remove_report "${meta_path}" |
226 continue | 292 continue |
227 fi | 293 fi |
228 if ! check_rate; then | 294 |
229 lecho "Sending ${report_path} would exceed rate. Leaving for later." | 295 if is_feedback_disabled; then |
| 296 lecho "Uploading is disabled. Removing crash." |
| 297 remove_report "${meta_path}" |
| 298 continue |
| 299 fi |
| 300 |
| 301 if ! is_mock && ! is_official; then |
| 302 lecho "Not an official OS version. Removing crash." |
| 303 remove_report "${meta_path}" |
| 304 continue |
| 305 fi |
| 306 |
| 307 if is_on_3g; then |
| 308 lecho "Not sending crash reports while on 3G, saving for later." |
230 return 0 | 309 return 0 |
231 fi | 310 fi |
232 local chromeos_version=$(get_version) | 311 |
233 if is_feedback_disabled; then | 312 if ! is_complete_metadata "${meta_path}"; then |
234 lecho "Uploading is disabled. Removing crash." | 313 # This report is incomplete, so if it's old, just remove it. |
235 rm "${report_path}" | 314 local old_meta=$(${FIND} "${dir}" -mindepth 1 -name \ |
236 elif is_on_3g; then | 315 $(basename "${meta_path}") -mmin +$((24 * 60)) -type f) |
237 lecho "Not sending crash report while on 3G, saving for later." | 316 if [ -n "${old_meta}" ]; then |
238 elif send_crash "${report_path}"; then | 317 lecho "Removing old incomplete metadata." |
239 # Send was successful, now remove | 318 remove_report "${meta_path}" |
240 lecho "Successfully sent crash ${report_path} and removing" | 319 else |
241 rm "${report_path}" | 320 lecho "Ignoring recent incomplete metadata." |
242 else | 321 fi |
243 lecho "Problem sending ${report_path}, not removing" | 322 continue |
244 fi | 323 fi |
| 324 |
| 325 if ! check_rate; then |
| 326 lecho "Sending ${meta_path} would exceed rate. Leaving for later." |
| 327 return 0 |
| 328 fi |
| 329 |
| 330 if ! send_crash "${meta_path}"; then |
| 331 lecho "Problem sending ${meta_path}, not removing." |
| 332 continue |
| 333 fi |
| 334 |
| 335 # Send was successful, now remove. |
| 336 lecho "Successfully sent crash ${meta_path} and removing." |
| 337 remove_report "${meta_path}" |
245 done | 338 done |
246 } | 339 } |
247 | 340 |
248 main() { | 341 main() { |
249 trap cleanup EXIT INT TERM | 342 trap cleanup EXIT INT TERM |
250 if [ -e "${PAUSE_CRASH_SENDING}" ]; then | 343 if [ -e "${PAUSE_CRASH_SENDING}" ]; then |
251 lecho "Exiting early due to ${PAUSE_CRASH_SENDING}" | 344 lecho "Exiting early due to ${PAUSE_CRASH_SENDING}." |
252 exit 1 | 345 exit 1 |
253 fi | 346 fi |
254 | 347 |
255 check_not_already_running | 348 check_not_already_running |
256 | 349 |
257 if [ ! -x "${FIND}" ]; then | 350 if [ ! -x "${FIND}" ]; then |
258 lecho "Fatal: Crash sending disabled: ${FIND} not found." | 351 lecho "Fatal: Crash sending disabled: ${FIND} not found." |
259 exit 1 | 352 exit 1 |
260 fi | 353 fi |
261 | 354 |
262 TMP_DIR="$(mktemp -d /tmp/crash_sender.XXXX)" | 355 TMP_DIR="$(mktemp -d /tmp/crash_sender.XXXX)" |
263 | 356 |
264 # Send system-wide crashes | 357 # Send system-wide crashes |
265 send_crashes "/var/spool/crash" | 358 send_crashes "/var/spool/crash" |
266 | 359 |
267 # Send user-specific crashes | 360 # Send user-specific crashes |
268 send_crashes "/home/chronos/user/crash" | 361 send_crashes "/home/chronos/user/crash" |
269 } | 362 } |
270 | 363 |
271 main | 364 main |
OLD | NEW |