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