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