NativeTombstoneManager
NativeTombstoneManager负责进行Java层tombstone写入dropbox。
NativeTombstoneManager在NativeTombstoneManagerService执行onStart方法的时候初始化,执行onBootPhase方法的时候执行onSystemReady方法。
base/services/java/com/android/server/SystemServer.java startCoreServices方法
// Tracks native tombstones.
t.traceBegin("StartNativeTombstoneManagerService");
mSystemServiceManager.startService(NativeTombstoneManagerService.class);
t.traceEnd();
@Override
public void onStart() {
mManager = new NativeTombstoneManager(getContext());
LocalServices.addService(NativeTombstoneManager.class, mManager);
}
@Override
public void onBootPhase(int phase) {
if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) {
mManager.onSystemReady();
}
}
在初始化的时候做了三件事情:
- 创建类型为SparseArray<TombstoneFile>的mTombstones数据结构
- 创建名为NativeTombstoneManager:tombstoneWatcher的线程,线程Handler命名为mHandler.
- 创建TombstoneWatcher,对/data/tombstones目录进行监听。当有tombstone发生时,会在该目录下生成tombstone文件和tombstone.pb文件,也就是说一次tombstone生成两个文件。当TombstoneWatcher监听到tombstone文件生成的时候会通过mHandler对每个文件执行handleTombstone方法。
NativeTombstoneManager(Context context) {
mTombstones = new SparseArray<TombstoneFile>();
mContext = context;
final ServiceThread thread = new ServiceThread(TAG + ":tombstoneWatcher",
THREAD_PRIORITY_BACKGROUND, true /* allowIo */);
thread.start();
mHandler = thread.getThreadHandler();
mWatcher = new TombstoneWatcher();
mWatcher.startWatching();
}
class TombstoneWatcher extends FileObserver {
TombstoneWatcher() {
// Tombstones can be created either by linking an O_TMPFILE temporary file (CREATE),
// or by moving a named temporary file in the same directory on kernels where O_TMPFILE
// isn't supported (MOVED_TO).
super(TOMBSTONE_DIR, FileObserver.CREATE | FileObserver.MOVED_TO);
}
@Override
public void onEvent(int event, @Nullable String path) {
mHandler.post(() -> {
handleTombstone(new File(TOMBSTONE_DIR, path));
});
}
}
在onSystemReady方法执行的时候,会通过mHandler异步遍历/data/tombstones下的文件,并对每个文件执行handleTombstone方法。
handleTombstone
private void handleTombstone(File path) {
//校验文件名称,如果不以tombstone_开头 ,就返回。
final String filename = path.getName();
if (!filename.startsWith("tombstone_")) {
return;
}
//processName默认为UNKNOWN
String processName = "UNKNOWN";
//如果文件以.pb结尾,就是protoFile
final boolean isProtoFile = filename.endsWith(".pb");
//这里会获取protoFile的路径
File protoPath = isProtoFile ? path : new File(path.getAbsolutePath() + ".pb");
//执行handleProtoTombstone方法处理proto文件,方法参数为文件路径(File类型)和是否是proto文件(bool类型),方法返回TombstoneFile
Optional<TombstoneFile> parsedTombstone = handleProtoTombstone(protoPath, isProtoFile);
//文件存在就获取processName
if (parsedTombstone.isPresent()) {
processName = parsedTombstone.get().getProcessName();
}
//把tombstone写入dropbox
BootReceiver.addTombstoneToDropBox(mContext, path, isProtoFile, processName);
}
主要做了两件事情:
- 利用handleProtoTombstone处理proto文件,把文件的信息存储到TombstoneFile,利用该数据结构获取processName
- 利用addTombstoneToDropBox把tombstone proto文件和普通tombstone文件写入dropbox
handleProtoTombstone
//有两个参数,第一个参数为文件(File类型),第二个参数为是否添加该tombstone到mTombstones数据结构中
private Optional<TombstoneFile> handleProtoTombstone(File path, boolean addToList) {
final String filename = path.getName();
if (!filename.endsWith(".pb")) {
Slog.w(TAG, "unexpected tombstone name: " + path);
return Optional.empty();
}
final String suffix = filename.substring("tombstone_".length());
final String numberStr = suffix.substring(0, suffix.length() - 3);
int number;
try {
number = Integer.parseInt(numberStr);
if (number < 0 || number > 99) {
Slog.w(TAG, "unexpected tombstone name: " + path);
return Optional.empty();
}
} catch (NumberFormatException ex) {
Slog.w(TAG, "unexpected tombstone name: " + path);
return Optional.empty();
}
ParcelFileDescriptor pfd;
try {
//使用ParcelFileDescriptor.open打开该pb文件
pfd = ParcelFileDescriptor.open(path, MODE_READ_WRITE);
} catch (FileNotFoundException ex) {
Slog.w(TAG, "failed to open " + path, ex);
return Optional.empty();
}
//使用TombstoneFile.parse解析该proto文件的内容到TombstoneFile数据结构中
final Optional<TombstoneFile> parsedTombstone = TombstoneFile.parse(pfd);
if (!parsedTombstone.isPresent()) {
IoUtils.closeQuietly(pfd);
return Optional.empty();
}
//如果需要添加该tombstone.pb到mTombstones中,那么就以tombstone发生的次序号为key,TombstoneFile信息为value存储到mTombstones中
if (addToList) {
synchronized (mLock) {
TombstoneFile previous = mTombstones.get(number);
if (previous != null) {
previous.dispose();
}
mTombstones.put(number, parsedTombstone.get());
}
}
return parsedTombstone;
}
主要做了两件事情:
- 打开tombstone proto文件,然后解析该文件,把内容解析到TombstoneFile数据结构中
- 添加该TombstoneFile信息到mTombstones中
addTombstoneToDropBox
//负责把tombstone文件(tombstone文件和tombstone.pb文件)写入dropbox
public static void addTombstoneToDropBox(
Context ctx, File tombstone, boolean proto, String processName) {
final DropBoxManager db = ctx.getSystemService(DropBoxManager.class);
if (db == null) {
Slog.e(TAG, "Can't log tombstone: DropBoxManager not available");
return;
}
// 对tombstone文件的写入进行速度限制,有专门的文档讲解,这么不再多说
// 要强调的是:一次tombstone的发生,会写入两次dropbox,分别是tombstone文件和tombstone.pb文件的写入
// 所以在速度限制上:tombstone的约束为10分钟只能写入6次,即10分钟内第四个tombstone发生的时候,就无法再写入tombstone
// 详细的规则参考 DropboxRateLimiter源码分析文档
// Check if we should rate limit and abort early if needed. Do this for both proto and
// non-proto tombstones, even though proto tombstones do not support including the counter
// of events dropped since rate limiting activated yet.
DropboxRateLimiter.RateLimitResult rateLimitResult =
sDropboxRateLimiter.shouldRateLimit(TAG_TOMBSTONE, processName);
if (rateLimitResult.shouldRateLimit()) return;
//从data/system/log-files.xml中读取已记录的错误信息
HashMap<String, Long> timestamps = readTimestamps();
try {
//如果是tombstone.pb文件
if (proto) {
//如果之前没有记录过(recordFileTimestamp返回true),即之前没有把该错误信息写到dropbox,那么就调用dropboxmanager进行写入
if (recordFileTimestamp(tombstone, timestamps)) {
db.addFile(TAG_TOMBSTONE_PROTO, tombstone, 0);
}
} else {
//如果是普通tombstone文件,需要添加头信息 + 其他信息到dropbox文件,即需要特殊处理,不能直接写入
// Add the header indicating how many events have been dropped due to rate limiting.
//添加头信息
final String headers = getBootHeadersToLogAndUpdate()
+ rateLimitResult.createHeader();
//调用addFileToDropBox,将头信息+其他信息写到dropbox文件
addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE,
TAG_TOMBSTONE);
}
} catch (IOException e) {
Slog.e(TAG, "Can't log tombstone", e);
}
//如果之前没有写入过dropbox,即没有记录过,即data/system/log-files.xml中没有该错误的信息,那么就会将错误的信息进行记录
//写属于覆盖写,而不是追加写,保证文件不会有重复内容。
writeTimestamps(timestamps);
}
private static final String LOG_FILES_FILE = "log-files.xml";
private static final AtomicFile sFile = new AtomicFile(new File(
Environment.getDataSystemDirectory(), LOG_FILES_FILE), "log-files");
readTimestamps
private static HashMap<String, Long> readTimestamps() {
synchronized (sFile) {
HashMap<String, Long> timestamps = new HashMap<String, Long>();
boolean success = false;
try (final FileInputStream stream = sFile.openRead()) {
TypedXmlPullParser parser = Xml.resolvePullParser(stream);
int type;
while ((type = parser.next()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
;
}
if (type != XmlPullParser.START_TAG) {
throw new IllegalStateException("no start tag found");
}
int outerDepth = parser.getDepth(); // Skip the outer <log-files> tag.
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if (tagName.equals("log")) {
//从sFile中查询所有的filename + timestamp,存储到timestamps中并返回
final String filename = parser.getAttributeValue(null, "filename");
final long timestamp = parser.getAttributeLong(null, "timestamp");
timestamps.put(filename, timestamp);
} else {
Slog.w(TAG, "Unknown tag: " + parser.getName());
XmlUtils.skipCurrentTag(parser);
}
}
success = true;
} catch (FileNotFoundException e) {
Slog.i(TAG, "No existing last log timestamp file " + sFile.getBaseFile() +
"; starting empty");
} catch (IOException e) {
Slog.w(TAG, "Failed parsing " + e);
} catch (IllegalStateException e) {
Slog.w(TAG, "Failed parsing " + e);
} catch (NullPointerException e) {
Slog.w(TAG, "Failed parsing " + e);
} catch (XmlPullParserException e) {
Slog.w(TAG, "Failed parsing " + e);
} finally {
if (!success) {
timestamps.clear();
}
}
return timestamps;
}
}
方法负责从sFile中查询所有的filename + timestamp,存储到timestamps中并返回
recordFileTimestamp
private static boolean recordFileTimestamp(File file, HashMap<String, Long> timestamps) {
final long fileTime = file.lastModified();
if (fileTime <= 0) return false; // File does not exist
final String filename = file.getPath();
if (timestamps.containsKey(filename) && timestamps.get(filename) == fileTime) {
return false; // Already logged this particular file
}
timestamps.put(filename, fileTime);
return true;
}
如果readTimestamps中已经记录过此错误的信息(即已写过dropbox),那么就返回false,否则添加此错误的信息到timestamps。
writeTimestamps
private static void writeTimestamps(HashMap<String, Long> timestamps) {
synchronized (sFile) {
final FileOutputStream stream;
try {
stream = sFile.startWrite();
} catch (IOException e) {
Slog.w(TAG, "Failed to write timestamp file: " + e);
return;
}
try {
TypedXmlSerializer out = Xml.resolveSerializer(stream);
out.startDocument(null, true);
out.startTag(null, "log-files");
Iterator<String> itor = timestamps.keySet().iterator();
while (itor.hasNext()) {
// 负责把timestamps的数据写回文件
String filename = itor.next();
out.startTag(null, "log");
out.attribute(null, "filename", filename);
out.attributeLong(null, "timestamp", timestamps.get(filename));
out.endTag(null, "log");
}
out.endTag(null, "log-files");
out.endDocument();
sFile.finishWrite(stream);
} catch (IOException e) {
Slog.w(TAG, "Failed to write timestamp file, using the backup: " + e);
sFile.failWrite(stream);
}
}
}
负责把timestamps的数据写回文件。
所以readTimestamps + recordFileTimestamp + writeTimestamps 保证了相同tombstone文件不会重复写dropbox。
注意:以下内容决定此次写属于覆盖写,而不是追加写,保证文件中不会有重复记录。
stream = sFile.startWrite();
// ...
out.startDocument(null, true);
getBootHeadersToLogAndUpdate
private static String getBootHeadersToLogAndUpdate() throws IOException {
final String oldHeaders = getPreviousBootHeaders();
final String newHeaders = getCurrentBootHeaders();
try {
FileUtils.stringToFile(lastHeaderFile, newHeaders);
} catch (IOException e) {
Slog.e(TAG, "Error writing " + lastHeaderFile, e);
}
if (oldHeaders == null) {
// If we failed to read the old headers, use the current headers
// but note this in the headers so we know
return "isPrevious: false\n" + newHeaders;
}
return "isPrevious: true\n" + oldHeaders;
}
private static String getPreviousBootHeaders() {
try {
return FileUtils.readTextFile(lastHeaderFile, 0, null);
} catch (IOException e) {
return null;
}
}
private static String getCurrentBootHeaders() throws IOException {
return new StringBuilder(512)
.append("Build: ").append(Build.FINGERPRINT).append("\n")
.append("Hardware: ").append(Build.BOARD).append("\n")
.append("Revision: ")
.append(SystemProperties.get("ro.revision", "")).append("\n")
.append("Bootloader: ").append(Build.BOOTLOADER).append("\n")
.append("Radio: ").append(Build.getRadioVersion()).append("\n")
.append("Kernel: ")
.append(FileUtils.readTextFile(new File("/proc/version"), 1024, "...\n"))
.append("\n").toString();
}
private static final String LAST_HEADER_FILE = "last-header.txt";
private static final File lastHeaderFile = new File(
Environment.getDataSystemDirectory(), LAST_HEADER_FILE);
从data/system/last-header.txt中读取boot header信息,我们称之为旧的boot header信息。
- 用新的boot header信息替换data/system/last-header.txt中的boot header信息
- 如果旧的不为空,返回"isPrevious: true\n" + oldHeaders,如果为空,返回"isPrevious: false\n" + newHeaders
rateLimitResult.createHeader
指的是被速度限制规则丢弃的错误次数。
addFileToDropBox
private static final int LOG_SIZE =
SystemProperties.getInt("ro.debuggable", 0) == 1 ? 98304 : 65536;
private static final String TAG_TOMBSTONE = "SYSTEM_TOMBSTONE";
addFileToDropBox(db, timestamps, headers, tombstone.getPath(), LOG_SIZE,
TAG_TOMBSTONE);
addFileToDropBox方法又调用了addFileWithFootersToDropBox,addFileWithFootersToDropBox增加了footers参数,此时footers参数为空字符串。
private static void addFileToDropBox(
DropBoxManager db, HashMap<String, Long> timestamps,
String headers, String filename, int maxSize, String tag) throws IOException {
addFileWithFootersToDropBox(db, timestamps, headers, "", filename, maxSize, tag);
}
addFileWithFootersToDropBox
private static void addFileWithFootersToDropBox(
DropBoxManager db, HashMap<String, Long> timestamps,
String headers, String footers, String filename, int maxSize,
String tag) throws IOException {
if (db == null || !db.isTagEnabled(tag)) return; // Logging disabled
File file = new File(filename);
//已经记录过,即已经写过dropbox,就返回
if (!recordFileTimestamp(file, timestamps)) {
return;
}
//对tombstone文件的内容进行截取,只保留maxSize的大小。
String fileContents = FileUtils.readTextFile(file, maxSize, TAG_TRUNCATED);
String text = headers + fileContents + footers;
//创建一个额外的dropbox文件,dropbox文件名为system_server_native_crash
// Create an additional report for system server native crashes, with a special tag.
if (tag.equals(TAG_TOMBSTONE) && fileContents.contains(">>> system_server <<<")) {
addTextToDropBox(db, "system_server_native_crash", text, filename, maxSize);
}
if (tag.equals(TAG_TOMBSTONE)) {
FrameworkStatsLog.write(FrameworkStatsLog.TOMB_STONE_OCCURRED);
}
//将tombstone写入dropbox
addTextToDropBox(db, tag, text, filename, maxSize);
}
主要做了以下4个事情:
- 不再写入已经写入过dropbox的错误。
- 对tombstone的内容进行截取,并把header与tombstone内容进行合并
- 使用addTextToDropBox生成额外的dropbox文件,名为system_server_native_crash
- 使用addTextToDropBox将tombstone写入dropbox
addTextToDropBox
private static void addTextToDropBox(DropBoxManager db, String tag, String text,
String filename, int maxSize) {
Slog.i(TAG, "Copying " + filename + " to DropBox (" + tag + ")");
db.addText(tag, text);
EventLog.writeEvent(DropboxLogTags.DROPBOX_FILE_COPY, filename, maxSize, tag);
}
主要做了两件事情:
- addTextToDropBox方法使用DropBoxManager的addText进行dropbox文件的生成。
- 写一个event log,此event log定义于 base/core/java/com/android/server/DropboxLogTags.logtags
option java_package com.android.server;
# -----------------------------
# BootReceiver.java
# -----------------------------
81002 dropbox_file_copy (FileName|3),(Size|1),(Tag|3)
TAG 为dropbox_file_copy,日志内容有3个,分别是FileName、Size、Tag。
可以在out下查看生成的DropboxLogTags.java,可参考:
./soong/.intermediates/frameworks/base/framework-minus-apex/android_common/gen/logtags/frameworks/base/core/java/com/android/server/DropboxLogTags.java
package com.android.server;;
/**
* @hide
*/
public class DropboxLogTags {
private DropboxLogTags() { } // don't instantiate
/** 81002 dropbox_file_copy (FileName|3),(Size|1),(Tag|3) */
public static final int DROPBOX_FILE_COPY = 81002;
public static void writeDropboxFileCopy(String filename, int size, String tag) {
android.util.EventLog.writeEvent(DROPBOX_FILE_COPY, filename, size, tag);
}
}
示例如下:
02-21 14:40:37.326 2395 2832 I dropbox_file_copy: [/data/tombstones/tombstone_08,98304,SYSTEM_TOMBSTONE]
网友评论