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 17 package com.android.server.wifi; 18 19 20 import android.util.ArrayMap; 21 22 import com.android.internal.annotations.VisibleForTesting; 23 import com.android.server.wifi.util.FileUtils; 24 25 import java.io.File; 26 import java.io.FileInputStream; 27 import java.io.IOException; 28 import java.io.PrintWriter; 29 import java.nio.file.Files; 30 import java.nio.file.Paths; 31 import java.util.Map; 32 33 /** 34 * Provides a facility for capturing kernel trace events related to Wifi control and data paths. 35 */ 36 public class LastMileLogger { LastMileLogger(WifiInjector injector)37 public LastMileLogger(WifiInjector injector) { 38 File tracefsEnablePath = new File(WIFI_EVENT_ENABLE_PATH); 39 if (tracefsEnablePath.exists()) { 40 initLastMileLogger(injector, WIFI_EVENT_BUFFER_PATH, WIFI_EVENT_ENABLE_PATH, 41 WIFI_EVENT_RELEASE_PATH); 42 } else { 43 initLastMileLogger(injector, WIFI_EVENT_BUFFER_PATH_DEBUGFS, 44 WIFI_EVENT_ENABLE_PATH_DEBUGFS, WIFI_EVENT_RELEASE_PATH_DEBUGFS); 45 } 46 } 47 48 @VisibleForTesting LastMileLogger(WifiInjector injector, String bufferPath, String enablePath, String releasePath)49 public LastMileLogger(WifiInjector injector, String bufferPath, String enablePath, 50 String releasePath) { 51 initLastMileLogger(injector, bufferPath, enablePath, releasePath); 52 } 53 54 /** 55 * Informs LastMileLogger that a connection event has occurred. 56 * @param event an event defined in WifiDiagnostics 57 */ reportConnectionEvent(String ifaceName, byte event)58 public void reportConnectionEvent(String ifaceName, byte event) { 59 boolean wasTracingEnabled = anyConnectionInProgress(); 60 61 mIfaceToConnectionStatus.put(ifaceName, event); 62 63 boolean shouldTracingBeEnabled = anyConnectionInProgress(); 64 65 if (!wasTracingEnabled && shouldTracingBeEnabled) { 66 enableTracing(); 67 } else if (wasTracingEnabled && !shouldTracingBeEnabled) { 68 disableTracing(); 69 } 70 71 if (event == WifiDiagnostics.CONNECTION_EVENT_FAILED 72 || event == WifiDiagnostics.CONNECTION_EVENT_TIMEOUT) { 73 mLastMileLogForLastFailure = readTrace(); 74 } 75 } 76 anyConnectionInProgress()77 private boolean anyConnectionInProgress() { 78 for (byte status : mIfaceToConnectionStatus.values()) { 79 if (status == WifiDiagnostics.CONNECTION_EVENT_STARTED) { 80 return true; 81 } 82 } 83 return false; 84 } 85 86 /** 87 * Dumps the contents of the log. 88 * @param pw the PrintWriter that will receive the dump 89 */ dump(PrintWriter pw)90 public void dump(PrintWriter pw) { 91 dumpInternal(pw, "Last failed last-mile log", mLastMileLogForLastFailure); 92 dumpInternal(pw, "Latest last-mile log", readTrace()); 93 } 94 95 private static final String TAG = "LastMileLogger"; 96 private static final String WIFI_EVENT_BUFFER_PATH = 97 "/sys/kernel/tracing/instances/wifi/trace"; 98 private static final String WIFI_EVENT_ENABLE_PATH = 99 "/sys/kernel/tracing/instances/wifi/tracing_on"; 100 private static final String WIFI_EVENT_RELEASE_PATH = 101 "/sys/kernel/tracing/instances/wifi/free_buffer"; 102 private static final String WIFI_EVENT_BUFFER_PATH_DEBUGFS = 103 "/sys/kernel/debug/tracing/instances/wifi/trace"; 104 private static final String WIFI_EVENT_ENABLE_PATH_DEBUGFS = 105 "/sys/kernel/debug/tracing/instances/wifi/tracing_on"; 106 private static final String WIFI_EVENT_RELEASE_PATH_DEBUGFS = 107 "/sys/kernel/debug/tracing/instances/wifi/free_buffer"; 108 109 private String mEventBufferPath; 110 private String mEventEnablePath; 111 private String mEventReleasePath; 112 private WifiLog mLog; 113 private byte[] mLastMileLogForLastFailure; 114 private FileInputStream mLastMileTraceHandle; 115 /** 116 * String key: iface name 117 * byte value: Connection status, one of WifiDiagnostics.CONNECTION_EVENT_* 118 */ 119 private final Map<String, Byte> mIfaceToConnectionStatus = new ArrayMap<>(); 120 initLastMileLogger(WifiInjector injector, String bufferPath, String enablePath, String releasePath)121 private void initLastMileLogger(WifiInjector injector, String bufferPath, String enablePath, 122 String releasePath) { 123 mLog = injector.makeLog(TAG); 124 mEventBufferPath = bufferPath; 125 mEventEnablePath = enablePath; 126 mEventReleasePath = releasePath; 127 } 128 enableTracing()129 private void enableTracing() { 130 if (!ensureFailSafeIsArmed()) { 131 mLog.wC("Failed to arm fail-safe."); 132 return; 133 } 134 135 try { 136 FileUtils.stringToFile(mEventEnablePath, "1"); 137 } catch (IOException e) { 138 mLog.warn("Failed to start event tracing: %").r(e.getMessage()).flush(); 139 } 140 } 141 disableTracing()142 private void disableTracing() { 143 try { 144 FileUtils.stringToFile(mEventEnablePath, "0"); 145 } catch (IOException e) { 146 mLog.warn("Failed to stop event tracing: %").r(e.getMessage()).flush(); 147 } 148 } 149 readTrace()150 private byte[] readTrace() { 151 try { 152 return Files.readAllBytes(Paths.get(mEventBufferPath)); 153 } catch (IOException e) { 154 mLog.warn("Failed to read event trace: %").r(e.getMessage()).flush(); 155 return new byte[0]; 156 } 157 } 158 ensureFailSafeIsArmed()159 private boolean ensureFailSafeIsArmed() { 160 if (mLastMileTraceHandle != null) { 161 return true; 162 } 163 164 try { 165 // This file provides fail-safe behavior for Last-Mile logging. Given that we: 166 // 1. Set the disable_on_free option in the trace_options pseudo-file 167 // (see wifi-events.rc), and 168 // 2. Hold the WIFI_EVENT_RELEASE_PATH open, 169 // 170 // Then, when this process dies, the kernel will automatically disable any 171 // tracing in the wifi trace instance. 172 // 173 // Note that, despite Studio's suggestion that |mLastMileTraceHandle| could be demoted 174 // to a local variable, we need to stick with a field. Otherwise, the handle could be 175 // garbage collected. 176 mLastMileTraceHandle = new FileInputStream(mEventReleasePath); 177 return true; 178 } catch (IOException e) { 179 mLog.warn("Failed to open free_buffer pseudo-file: %").r(e.getMessage()).flush(); 180 return false; 181 } 182 } 183 dumpInternal(PrintWriter pw, String description, byte[] lastMileLog)184 private static void dumpInternal(PrintWriter pw, String description, byte[] lastMileLog) { 185 if (lastMileLog == null || lastMileLog.length < 1) { 186 pw.format("No last mile log for \"%s\"\n", description); 187 return; 188 } 189 190 pw.format("-------------------------- %s ---------------------------\n", description); 191 pw.print(new String(lastMileLog)); 192 pw.println("--------------------------------------------------------------------"); 193 } 194 } 195