LastMileLogger.java revision 3355eb4392aabfae9453e7d6f11d9f0620bf5dae
1/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wifi;
18
19import android.os.FileUtils;
20
21import com.android.internal.annotations.VisibleForTesting;
22
23import libcore.io.IoUtils;
24
25import java.io.FileInputStream;
26import java.io.IOException;
27import java.io.PrintWriter;
28
29/**
30 * Provides a facility for capturing kernel trace events related to Wifi control and data paths.
31 */
32public class LastMileLogger {
33    public LastMileLogger(WifiInjector injector) {
34        this(injector, WIFI_EVENT_BUFFER_PATH, WIFI_EVENT_ENABLE_PATH, WIFI_EVENT_RELEASE_PATH);
35    }
36
37    @VisibleForTesting
38    public LastMileLogger(WifiInjector injector, String bufferPath, String enablePath,
39                          String releasePath) {
40        mLog = injector.makeLog(TAG);
41        mEventBufferPath = bufferPath;
42        mEventEnablePath = enablePath;
43        mEventReleasePath = releasePath;
44        try {
45            // This file provides fail-safe behavior for Last-Mile logging. Given that we:
46            // 1. Set the disable_on_free option in the trace_options pseudo-file
47            //    (see wifi-events.rc), and
48            // 2. Hold the WIFI_EVENT_RELEASE_PATH open,
49            //
50            // Then, when this process dies, the kernel will automatically disable any
51            // tracing in the wifi trace instance.
52            //
53            // Note that, despite Studio's suggestion that |mLastMileTraceHandle| could be demoted
54            // to a local variable, we need to stick with a field. Otherwise, the handle could be
55            // garbage collected.
56            mLastMileTraceHandle = new FileInputStream(mEventReleasePath);
57        } catch (IOException e) {
58            mLog.warn("Failed to open free_buffer pseudo-file: %").r(e.getMessage()).flush();
59        }
60    }
61
62    /**
63     * Informs LastMileLogger that a connection event has occurred.
64     * @param connectionId A non-negative connection identifier, or -1 to indicate unknown
65     * @param event an event defined in BaseWifiDiagnostics
66     */
67    public void reportConnectionEvent(long connectionId, byte event) {
68        if (connectionId < 0) {
69            mLog.warn("Ignoring negative connection id: %").c(connectionId);
70            return;
71        }
72
73        switch (event) {
74            case BaseWifiDiagnostics.CONNECTION_EVENT_STARTED:
75                mPendingConnectionId = connectionId;
76                enableTracing();
77                return;
78            case BaseWifiDiagnostics.CONNECTION_EVENT_SUCCEEDED:
79                mPendingConnectionId = -1;
80                disableTracing();
81                return;
82            case BaseWifiDiagnostics.CONNECTION_EVENT_FAILED:
83                if (connectionId >= mPendingConnectionId) {
84                    mPendingConnectionId = -1;
85                    disableTracing();
86                    mLastMileLogForLastFailure = readTrace();
87                    return;
88                } else {
89                    // Spurious failure message. Here's one scenario where this might happen:
90                    // t=00sec      start first connection attempt
91                    // t=30sec      start second connection attempt
92                    // t=60sec      timeout first connection attempt
93                    // We should not stop tracing in this case, since the second connection attempt
94                    // is still in progress.
95                    return;
96                }
97        }
98    }
99
100    /**
101     * Dumps the contents of the log.
102     * @param pw the PrintWriter that will receive the dump
103     */
104    public void dump(PrintWriter pw) {
105        dumpInternal(pw, "Last failed last-mile log", mLastMileLogForLastFailure);
106        dumpInternal(pw, "Latest last-mile log", readTrace());
107        mLastMileLogForLastFailure = null;
108    }
109
110    private static final String TAG = "LastMileLogger";
111    private static final String WIFI_EVENT_BUFFER_PATH =
112            "/sys/kernel/debug/tracing/instances/wifi/trace";
113    private static final String WIFI_EVENT_ENABLE_PATH =
114            "/sys/kernel/debug/tracing/instances/wifi/tracing_on";
115    private static final String WIFI_EVENT_RELEASE_PATH =
116            "/sys/kernel/debug/tracing/instances/wifi/free_buffer";
117
118    private final String mEventBufferPath;
119    private final String mEventEnablePath;
120    private final String mEventReleasePath;
121    private WifiLog mLog;
122    private byte[] mLastMileLogForLastFailure;
123    private FileInputStream mLastMileTraceHandle;
124    private long mPendingConnectionId = -1;
125
126    private void enableTracing() {
127        try {
128            FileUtils.stringToFile(mEventEnablePath, "1");
129        } catch (IOException e) {
130            mLog.warn("Failed to start event tracing: %").r(e.getMessage()).flush();
131        }
132    }
133
134    private void disableTracing() {
135        try {
136            FileUtils.stringToFile(mEventEnablePath, "0");
137        } catch (IOException e) {
138            mLog.warn("Failed to stop event tracing: %").r(e.getMessage()).flush();
139        }
140    }
141
142    private byte[] readTrace() {
143        try {
144            return IoUtils.readFileAsByteArray(mEventBufferPath);
145        } catch (IOException e) {
146            mLog.warn("Failed to read event trace: %").r(e.getMessage()).flush();
147            return new byte[0];
148        }
149    }
150
151    private static void dumpInternal(PrintWriter pw, String description, byte[] lastMileLog) {
152        if (lastMileLog == null || lastMileLog.length < 1) {
153            pw.format("No last mile log for \"%s\"\n", description);
154            return;
155        }
156
157        pw.format("-------------------------- %s ---------------------------\n", description);
158        pw.print(new String(lastMileLog));
159        pw.println("--------------------------------------------------------------------");
160    }
161}
162