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