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.cts.apicoverage; 18 19 import com.android.compatibility.common.util.ReadElf; 20 21 import org.w3c.dom.Attr; 22 import org.w3c.dom.Document; 23 import org.w3c.dom.Element; 24 import org.w3c.dom.Node; 25 26 import java.io.File; 27 import java.io.FileOutputStream; 28 import java.io.FilenameFilter; 29 import java.io.IOException; 30 import java.util.ArrayList; 31 import java.util.Arrays; 32 import java.util.HashMap; 33 import java.util.Iterator; 34 import java.util.List; 35 import java.util.Map; 36 37 import javax.xml.parsers.DocumentBuilder; 38 import javax.xml.parsers.DocumentBuilderFactory; 39 import javax.xml.parsers.ParserConfigurationException; 40 import javax.xml.transform.OutputKeys; 41 import javax.xml.transform.Transformer; 42 import javax.xml.transform.TransformerException; 43 import javax.xml.transform.TransformerFactory; 44 import javax.xml.transform.dom.DOMSource; 45 import javax.xml.transform.stream.StreamResult; 46 /** 47 * Class that outputs an XML report of the {@link ApiCoverage} collected. It can be viewed in a 48 * browser when used with the api-coverage.css and api-coverage.xsl files. 49 */ 50 class NdkApiXmlReport { 51 private static final String API_TAG = "api"; 52 private static final String PACKAGE_TAG = "package"; 53 private static final String CLASS_TAG = "class"; 54 private static final String METHOD_TAG = "method"; 55 private static final String FIELD_TAG = "field"; 56 private static final String ATTRIBUTE_NAME = "name"; 57 private static final String NDK_PACKAGE_NAME = "ndk"; 58 private static final String NDK_DUMMY_RETURN_TYPE = "na"; 59 60 private static final Map<String, String> sInternalSymMap; 61 static { 62 sInternalSymMap = new HashMap<String, String>(); 63 sInternalSymMap.put("__bss_start", "bss"); 64 sInternalSymMap.put("_end", "initialized data"); 65 sInternalSymMap.put("_edata", "uninitialized data"); 66 } 67 68 private static final FilenameFilter SUPPORTED_FILE_NAME_FILTER = 69 new FilenameFilter() { 70 public boolean accept(File dir, String name) { 71 String fileName = name.toLowerCase(); 72 return fileName.endsWith(".so"); 73 } 74 }; 75 printUsage()76 private static void printUsage() { 77 System.out.println("Usage: ndk-api-xml-report [OPTION]... [APK]..."); 78 System.out.println(); 79 System.out.println("Generates a report about what Android NDK methods."); 80 System.out.println(); 81 System.out.println("this must be used from the $ANDROID_BUILD_TOP"); 82 System.out.println("make cts-test-coverage"); 83 System.out.println("unzip the target ndk_platform.tar.bz2 to a folder."); 84 System.out.println( 85 "$ANDROID_HOST_OUT/bin/ndk-api-report " 86 + "-o $ANDROID_BUILD_TOP/cts/tools/cts-api-coverage/etc/ndk-api.xml " 87 + "-n <ndk-folder>/platforms/android-current/arch-arm64/usr/lib"); 88 System.out.println(); 89 System.out.println("Options:"); 90 System.out.println(" -o FILE output file or standard out if not given"); 91 System.out.println(" -n PATH path to the NDK Lib Folder"); 92 System.out.println(); 93 System.exit(1); 94 } 95 96 /** Get the argument or print out the usage and exit. */ getExpectedArg(String[] args, int index)97 private static String getExpectedArg(String[] args, int index) { 98 if (index < args.length) { 99 return args[index]; 100 } else { 101 printUsage(); 102 return null; // Never will happen because printUsage will call exit(1) 103 } 104 } 105 main(String[] args)106 public static void main(String[] args) throws IOException { 107 List<File> ndkSos = new ArrayList<File>(); 108 int numNdkSos = 0; 109 String ndkLibPath = ""; 110 String outputFilePath = "./ndk-api.xml"; 111 112 for (int i = 0; i < args.length; i++) { 113 if (args[i].startsWith("-")) { 114 if ("-o".equals(args[i])) { 115 outputFilePath = getExpectedArg(args, ++i); 116 } else if ("-n".equals(args[i])) { 117 ndkLibPath = getExpectedArg(args, ++i); 118 File file = new File(ndkLibPath); 119 if (file.isDirectory()) { 120 ndkSos.addAll(Arrays.asList(file.listFiles(SUPPORTED_FILE_NAME_FILTER))); 121 } else { 122 printUsage(); 123 } 124 } else { 125 printUsage(); 126 } 127 } else { 128 printUsage(); 129 } 130 } 131 132 Document dom; 133 // instance of a DocumentBuilderFactory 134 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 135 try { 136 // use factory to get an instance of document builder 137 DocumentBuilder db = dbf.newDocumentBuilder(); 138 // create instance of DOM 139 dom = db.newDocument(); 140 141 // create the root element 142 Element apiEle = dom.createElement(API_TAG); 143 Element pkgEle = dom.createElement(PACKAGE_TAG); 144 setAttribute(dom, pkgEle, ATTRIBUTE_NAME, NDK_PACKAGE_NAME); 145 apiEle.appendChild(pkgEle); 146 dom.appendChild(apiEle); 147 148 for (File ndkSo : ndkSos) { 149 ReadElf re = ReadElf.read(ndkSo); 150 re.getDynamicSymbol(""); 151 ReadElf.Symbol[] symArr = re.getDynSymArr(); 152 System.out.println(ndkSo.getName()); 153 Element classEle = addToDom(dom, pkgEle, symArr, ndkSo.getName().toLowerCase()); 154 pkgEle.appendChild(classEle); 155 } 156 157 try { 158 Transformer tr = TransformerFactory.newInstance().newTransformer(); 159 // enable indent in result file 160 tr.setOutputProperty(OutputKeys.INDENT, "yes"); 161 tr.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); 162 163 // send DOM to file 164 tr.transform( 165 new DOMSource(dom), new StreamResult(new FileOutputStream(outputFilePath))); 166 167 } catch (TransformerException te) { 168 System.out.println(te.getMessage()); 169 } catch (IOException ioe) { 170 System.out.println(ioe.getMessage()); 171 } 172 } catch (ParserConfigurationException pce) { 173 System.out.println("UsersXML: Error trying to instantiate DocumentBuilder " + pce); 174 } 175 } 176 addToDom( Document dom, Element pkgEle, ReadElf.Symbol[] symArr, String libName)177 public static Element addToDom( 178 Document dom, Element pkgEle, ReadElf.Symbol[] symArr, String libName) { 179 Element classEle = createClassEle(dom, libName); 180 for (int i = 0; i < symArr.length; i++) { 181 if (symArr[i].isExtern()) { 182 Element methodEle; 183 if(isInternalSymbol(symArr[i])) { 184 continue; 185 } 186 187 if (symArr[i].type == ReadElf.Symbol.STT_OBJECT) { 188 methodEle = createFieldEle(dom, symArr[i].name); 189 } else { 190 methodEle = createMethodEle(dom, symArr[i].name); 191 } 192 193 System.out.println(symArr[i].name); 194 classEle.appendChild(methodEle); 195 } 196 } 197 return classEle; 198 } 199 addToDom(Document dom, Element pkgEle, ReadElf.Symbol[] symArr)200 public static void addToDom(Document dom, Element pkgEle, ReadElf.Symbol[] symArr) { 201 HashMap<String, Element> classEleMap = new HashMap<String, Element>(); 202 for (int i = 0; i < symArr.length; i++) { 203 if (symArr[i].isExtern()) { 204 Element methodEle; 205 if (symArr[i].type == ReadElf.Symbol.STT_OBJECT) { 206 methodEle = createFieldEle(dom, symArr[i].name); 207 } else { 208 methodEle = createMethodEle(dom, symArr[i].name); 209 } 210 211 System.out.println(symArr[i].name); 212 213 // add to the class element 214 String libName = symArr[i].getVerDefLibName(); 215 Element classEle = classEleMap.get(libName); 216 if (classEle == null) { 217 classEle = createClassEle(dom, libName); 218 classEleMap.put(libName, classEle); 219 } 220 classEle.appendChild(methodEle); 221 } 222 } 223 Iterator ite = classEleMap.entrySet().iterator(); 224 while (ite.hasNext()) { 225 Map.Entry<String, Element> entry = (Map.Entry<String, Element>) ite.next(); 226 pkgEle.appendChild(entry.getValue()); 227 } 228 } 229 saveToXML(String xml, ReadElf.Symbol[] symArr)230 public static void saveToXML(String xml, ReadElf.Symbol[] symArr) { 231 Document dom; 232 Element ele = null; 233 234 // instance of a DocumentBuilderFactory 235 DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 236 try { 237 // use factory to get an instance of document builder 238 DocumentBuilder db = dbf.newDocumentBuilder(); 239 // create instance of DOM 240 dom = db.newDocument(); 241 242 // create the root element 243 Element apiEle = dom.createElement(API_TAG); 244 Element packageEle = dom.createElement(PACKAGE_TAG); 245 setAttribute(dom, packageEle, ATTRIBUTE_NAME, NDK_PACKAGE_NAME); 246 Element classEle = createClassEle(dom, "class"); 247 packageEle.appendChild(classEle); 248 apiEle.appendChild(packageEle); 249 dom.appendChild(apiEle); 250 251 for (int i = 0; i < symArr.length; i++) { 252 if (symArr[i].isExtern()) { 253 Element methodEle; 254 if (symArr[i].type == ReadElf.Symbol.STT_OBJECT) { 255 methodEle = createFieldEle(dom, symArr[i].name); 256 } else { 257 methodEle = createMethodEle(dom, symArr[i].name); 258 } 259 classEle.appendChild(methodEle); 260 } 261 } 262 263 try { 264 Transformer tr = TransformerFactory.newInstance().newTransformer(); 265 // enable indent in result file 266 tr.setOutputProperty(OutputKeys.INDENT, "yes"); 267 tr.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); 268 269 // send DOM to file 270 tr.transform(new DOMSource(dom), new StreamResult(new FileOutputStream(xml))); 271 272 } catch (TransformerException te) { 273 System.out.println(te.getMessage()); 274 } catch (IOException ioe) { 275 System.out.println(ioe.getMessage()); 276 } 277 } catch (ParserConfigurationException pce) { 278 System.out.println("UsersXML: Error trying to instantiate DocumentBuilder " + pce); 279 } 280 } 281 isInternalSymbol(ReadElf.Symbol sym)282 protected static boolean isInternalSymbol(ReadElf.Symbol sym) { 283 String value = sInternalSymMap.get(sym.name); 284 if (value == null) { 285 return false; 286 } else { 287 return true; 288 } 289 } 290 setAttribute(Document doc, Node elem, String name, String value)291 protected static void setAttribute(Document doc, Node elem, String name, String value) { 292 Attr attr = doc.createAttribute(name); 293 attr.setNodeValue(value); 294 elem.getAttributes().setNamedItem(attr); 295 } 296 createClassEle(Document doc, String name)297 protected static Element createClassEle(Document doc, String name) { 298 Element ele = doc.createElement(CLASS_TAG); 299 setAttribute(doc, ele, ATTRIBUTE_NAME, name); 300 setAttribute(doc, ele, "abstract", "false"); 301 setAttribute(doc, ele, "static", "false"); 302 setAttribute(doc, ele, "final", "true"); 303 setAttribute(doc, ele, "deprecated", "not deprecated"); 304 setAttribute(doc, ele, "visibility", "public"); 305 return ele; 306 } 307 createMethodEle(Document doc, String name)308 protected static Element createMethodEle(Document doc, String name) { 309 Element ele = doc.createElement(METHOD_TAG); 310 setAttribute(doc, ele, ATTRIBUTE_NAME, name); 311 setAttribute(doc, ele, "return", NDK_DUMMY_RETURN_TYPE); 312 setAttribute(doc, ele, "abstract", "false"); 313 setAttribute(doc, ele, "native", "true"); 314 setAttribute(doc, ele, "synchronized", "true"); 315 setAttribute(doc, ele, "static", "false"); 316 setAttribute(doc, ele, "final", "true"); 317 setAttribute(doc, ele, "deprecated", "not deprecated"); 318 setAttribute(doc, ele, "visibility", "public"); 319 return ele; 320 } 321 createFieldEle(Document doc, String name)322 protected static Element createFieldEle(Document doc, String name) { 323 Element ele = doc.createElement(FIELD_TAG); 324 setAttribute(doc, ele, ATTRIBUTE_NAME, name); 325 setAttribute(doc, ele, "type", "native"); 326 setAttribute(doc, ele, "transient", "false"); 327 setAttribute(doc, ele, "volatile", "false"); 328 setAttribute(doc, ele, "value", ""); 329 setAttribute(doc, ele, "static", "false"); 330 setAttribute(doc, ele, "final", "true"); 331 setAttribute(doc, ele, "deprecated", "not deprecated"); 332 setAttribute(doc, ele, "visibility", "public"); 333 return ele; 334 } 335 } 336