1 /* 2 * Copyright (C) 2020 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 package com.android.textclassifier.testing; 17 18 import java.io.BufferedReader; 19 import java.io.FileReader; 20 import java.io.IOException; 21 import java.util.HashMap; 22 import java.util.Map; 23 24 /** 25 * Class for reading a process' signal masks from the /proc filesystem. Looks for the 26 * BLOCKED, CAUGHT, IGNORED and PENDING masks from /proc/self/status, each of which is a 27 * 64 bit bitmask with one bit per signal. 28 * 29 * Maintains a map from SignalMaskInfo.Type to the bitmask. The {@code isValid} method 30 * will only return true if all 4 masks were successfully parsed. Provides lookup 31 * methods per signal, e.g. {@code isPending(signum)} which will throw 32 * {@code IllegalStateException} if the current data is not valid. 33 */ 34 public class SignalMaskInfo { 35 private enum Type { 36 BLOCKED("SigBlk"), 37 CAUGHT("SigCgt"), 38 IGNORED("SigIgn"), 39 PENDING("SigPnd"); 40 // The tag for this mask in /proc/self/status 41 private final String tag; 42 Type(String tag)43 Type(String tag) { 44 this.tag = tag + ":\t"; 45 } 46 getTag()47 public String getTag() { 48 return tag; 49 } 50 parseProcinfo(String path)51 public static Map<Type, Long> parseProcinfo(String path) { 52 Map<Type, Long> map = new HashMap<>(); 53 try (BufferedReader reader = new BufferedReader(new FileReader(path))) { 54 String line; 55 while ((line = reader.readLine()) != null) { 56 for (Type mask : values()) { 57 long value = mask.tryToParse(line); 58 if (value >= 0) { 59 map.put(mask, value); 60 } 61 } 62 } 63 } catch (NumberFormatException | IOException e) { 64 // Ignored - the map will end up being invalid instead. 65 } 66 return map; 67 } 68 tryToParse(String line)69 private long tryToParse(String line) { 70 if (line.startsWith(tag)) { 71 return Long.valueOf(line.substring(tag.length()), 16); 72 } else { 73 return -1; 74 } 75 } 76 } 77 78 private static final String PROCFS_PATH = "/proc/self/status"; 79 private Map<Type, Long> maskMap = null; 80 SignalMaskInfo()81 SignalMaskInfo() { 82 refresh(); 83 } 84 refresh()85 public void refresh() { 86 maskMap = Type.parseProcinfo(PROCFS_PATH); 87 } 88 isValid()89 public boolean isValid() { 90 return (maskMap != null && maskMap.size() == Type.values().length); 91 } 92 isCaught(int signal)93 public boolean isCaught(int signal) { 94 return isSignalInMask(signal, Type.CAUGHT); 95 } 96 isBlocked(int signal)97 public boolean isBlocked(int signal) { 98 return isSignalInMask(signal, Type.BLOCKED); 99 } 100 isPending(int signal)101 public boolean isPending(int signal) { 102 return isSignalInMask(signal, Type.PENDING); 103 } 104 isIgnored(int signal)105 public boolean isIgnored(int signal) { 106 return isSignalInMask(signal, Type.IGNORED); 107 } 108 checkValid()109 private void checkValid() { 110 if (!isValid()) { 111 throw new IllegalStateException(); 112 } 113 } 114 isSignalInMask(int signal, Type mask)115 private boolean isSignalInMask(int signal, Type mask) { 116 long bit = 1L << (signal - 1); 117 return (getSignalMask(mask) & bit) != 0; 118 } 119 getSignalMask(Type mask)120 private long getSignalMask(Type mask) { 121 checkValid(); 122 Long value = maskMap.get(mask); 123 if (value == null) { 124 throw new IllegalStateException(); 125 } 126 return value; 127 } 128 } 129