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