• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.releaseparser;
18 
19 import com.android.cts.releaseparser.ReleaseProto.*;
20 
21 import java.io.File;
22 import java.io.FileInputStream;
23 import java.io.FileWriter;
24 import java.io.IOException;
25 import java.io.PrintWriter;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.Map;
29 import java.util.TreeMap;
30 
31 public class DepCsvPrinter {
32     private ReleaseContent mRelContent;
33     private PrintWriter mPWriter;
34     private HashMap<String, String> mLibMap;
35     private HashMap<String, String> mDepPathMap;
36     private TreeMap<String, Entry> mTreeEntryMap;
37     private ReleaseContent mBRelContent;
38     private int mBits;
39     private String mTitle;
40     private int mCurLevel;
41     private ArrayList<Entry> mEntryList;
42 
getTitle(ReleaseContent relContent)43     private static String getTitle(ReleaseContent relContent) {
44         return relContent.getName() + relContent.getVersion() + relContent.getBuildNumber();
45     }
46 
DepCsvPrinter(ReleaseContent relContent)47     public DepCsvPrinter(ReleaseContent relContent) {
48         mRelContent = relContent;
49         mTitle = getTitle(relContent);
50         mBRelContent = null;
51         mTreeEntryMap = new TreeMap<String, Entry>(mRelContent.getEntries());
52     }
53 
writeDeltaDigraphs(ReleaseContent bRelContent, String dirName)54     public void writeDeltaDigraphs(ReleaseContent bRelContent, String dirName) {
55         mBRelContent = bRelContent;
56         mTitle = mTitle + "_vs_" + getTitle(bRelContent);
57         compareEntries(dirName);
58     }
59 
compareEntries(String dirName)60     public void compareEntries(String dirName) {
61         for (Entry entry : mRelContent.getEntries().values()) {
62             if (entry.getType() == Entry.EntryType.EXE) {
63                 String exeName = entry.getName();
64                 String fileName = String.format("%s/%s.csv", dirName, exeName);
65                 writeCsv(entry, fileName);
66             }
67         }
68     }
69 
writeDeltaDigraph(String exeName, ReleaseContent bRelContent, String fileName)70     public void writeDeltaDigraph(String exeName, ReleaseContent bRelContent, String fileName) {
71         mBRelContent = bRelContent;
72         mTitle = mTitle + "_vs_" + getTitle(bRelContent);
73         compareEntry(exeName, fileName);
74     }
75 
compareEntry(String exeName, String fileName)76     public void compareEntry(String exeName, String fileName) {
77         for (Entry entry : mRelContent.getEntries().values()) {
78             if (entry.getName().equals(exeName)) {
79                 writeCsv(entry, fileName);
80                 break;
81             }
82         }
83     }
84 
writeCsv(Entry entry, String fileName)85     public void writeCsv(Entry entry, String fileName) {
86         try {
87             String exeName = entry.getName();
88             FileWriter fWriter = new FileWriter(fileName);
89             mPWriter = new PrintWriter(fWriter);
90             mLibMap = new HashMap<String, String>();
91             mEntryList = new ArrayList<Entry>();
92             mDepPathMap = new HashMap<String, String>();
93             mBits = entry.getAbiBits();
94 
95             String sourceNode = getNodeName(exeName);
96             int delta = checkDelta(entry);
97 
98             mCurLevel = 0;
99             mEntryList.add(entry);
100             printDep(entry, sourceNode);
101 
102             mPWriter.println("id,label,value,delta");
103             for (Map.Entry<String, String> node : mLibMap.entrySet()) {
104                 mPWriter.println(String.format("%s,%s", node.getKey(), node.getValue()));
105             }
106 
107             mPWriter.println("source,target,value,label");
108             for (Map.Entry<String, String> link : mDepPathMap.entrySet()) {
109                 mPWriter.println(String.format("%s,%s", link.getKey(), link.getValue()));
110             }
111 
112             // Adjacency list
113             mPWriter.println("vertex,edge1,edge2,edge3,edge4,edge5,edge6,edge7,...");
114             for (Entry e : mEntryList) {
115                 mPWriter.println(
116                         String.format(
117                                 "%s,%s", e.getName(), String.join(",", e.getDependenciesList())));
118                 // String.join(",", e.getDynamicLoadingDependenciesList())));
119             }
120 
121             mPWriter.flush();
122             mPWriter.close();
123         } catch (IOException e) {
124             System.err.println("IOException:" + e.getMessage());
125         }
126     }
127 
writeRcDeltaDigraphs(ReleaseContent bRelContent, String dirName)128     public void writeRcDeltaDigraphs(ReleaseContent bRelContent, String dirName) {
129         mBRelContent = bRelContent;
130         mTitle = mTitle + "_vs_" + getTitle(bRelContent);
131         compareRcEntries(dirName);
132     }
133 
compareRcEntries(String dirName)134     public void compareRcEntries(String dirName) {
135         String fileName = String.format("%s/%s.csv", dirName, "RC-files");
136         try {
137             FileWriter fWriter = new FileWriter(fileName);
138             mPWriter = new PrintWriter(fWriter);
139             String rootNode = "root";
140             mPWriter.println("digraph {");
141             mPWriter.println("rankdir=LR;");
142             mPWriter.println("node [shape = box]");
143             mPWriter.println(String.format("%s [label=\"%s\"]", rootNode, getNodeName(mTitle)));
144 
145             for (Entry entry : mRelContent.getEntries().values()) {
146                 if (entry.getType() == Entry.EntryType.RC) {
147                     // only care if a RC starts Services
148                     if (entry.getDependenciesList().size() > 0) {
149                         writeRcDigraph(entry, rootNode);
150                     }
151                 }
152             }
153 
154             mPWriter.println("}");
155             mPWriter.flush();
156             mPWriter.close();
157         } catch (IOException e) {
158             System.err.println("IOException:" + e.getMessage());
159         }
160     }
161 
writeRcDigraph(Entry entry, String rootNode)162     public void writeRcDigraph(Entry entry, String rootNode) {
163         String rcName = entry.getRelativePath();
164         String sourceNode = getNodeName(rcName);
165         mPWriter.printf(String.format("%s [label=\"%s\"", sourceNode, rcName));
166         checkDelta(entry);
167         mPWriter.println("]");
168 
169         mPWriter.println(String.format("%s -> %s", rootNode, sourceNode));
170 
171         for (Service target : entry.getServicesList()) {
172             String targetFile =
173                     target.getFile().replace("/system/", "SYSTEM/").replace("/vendor/", "VENDOR/");
174             String targetNode = getNodeName(targetFile);
175             mPWriter.printf(String.format("%s [label=\"%s\"", targetNode, targetFile));
176             Entry targetEntry = mRelContent.getEntries().get(targetFile);
177             if (targetEntry != null) {
178                 checkDelta(targetEntry);
179             }
180             mPWriter.println("]");
181 
182             String depPath = String.format("%s -> %s", sourceNode, targetNode);
183             mPWriter.println(depPath);
184         }
185     }
186 
checkDelta(Entry srcEntry)187     private int checkDelta(Entry srcEntry) {
188         // compare
189         if (mBRelContent != null) {
190             Entry bEntry = mBRelContent.getEntries().get(srcEntry.getRelativePath());
191             if (bEntry == null) {
192                 // New Entry
193                 return -1;
194             } else {
195                 if (srcEntry.getContentId().equals(bEntry.getContentId())) {
196                     // Same
197                     return 0;
198                 } else {
199                     // Different
200                     return 1;
201                 }
202             }
203         }
204         return -2;
205     }
206 
getNodeName(String note)207     private String getNodeName(String note) {
208         return note;
209     }
210 
printDep(Entry srcEntry, String sourceNode)211     private void printDep(Entry srcEntry, String sourceNode) {
212         mCurLevel += 1;
213         ArrayList<String> allDepList = new ArrayList<>();
214         allDepList.addAll(srcEntry.getDependenciesList());
215         int depCnt = allDepList.size();
216         allDepList.addAll(srcEntry.getDynamicLoadingDependenciesList());
217         int no = 0;
218 
219         for (String dep : allDepList) {
220             boolean goFurther = false;
221             String targetNode;
222             Entry depEntry = null;
223             String filePath = dep;
224 
225             // libEGL*.so to VENDOR/lib64/egl/libEGL_adreno.so
226             int idx = dep.indexOf("*.so");
227             if (idx > -1) {
228                 if (mBits == 32) {
229                     filePath = "VENDOR/lib/egl/" + dep.substring(0, idx);
230                 } else {
231                     filePath = "VENDOR/lib64/egl/" + dep.substring(0, idx);
232                 }
233                 depEntry = mTreeEntryMap.tailMap(filePath, false).firstEntry().getValue();
234                 dep = depEntry.getName();
235             }
236             targetNode = getNodeName(dep);
237 
238             String depPath;
239             if (no < depCnt) {
240                 depPath = String.format("%s,%s,black", sourceNode, targetNode);
241             } else {
242                 // This is Dyanmic Loading
243                 depPath = String.format("%s,%s,steelblue", sourceNode, targetNode);
244             }
245 
246             if (mDepPathMap.get(depPath) == null) {
247                 // Print path once only
248                 mDepPathMap.put(depPath, String.format("%d", 1));
249             }
250 
251             if (depEntry == null) {
252                 if (dep.startsWith("/system")) {
253                     depEntry = mRelContent.getEntries().get(dep.replace("/system/", "SYSTEM/"));
254                 } else if (dep.startsWith("/vendor")) {
255                     depEntry = mRelContent.getEntries().get(dep.replace("/vendor/", "VENDOR/"));
256                 } else {
257                     if (mBits == 32) {
258                         filePath = String.format("SYSTEM/lib/%s", dep);
259                     } else {
260                         filePath = String.format("SYSTEM/lib64/%s", dep);
261                     }
262 
263                     depEntry = mRelContent.getEntries().get(filePath);
264 
265                     if (depEntry == null) {
266                         // try Vendor
267                         if (mBits == 32) {
268                             filePath = String.format("VENDOR/lib/%s", dep);
269                         } else {
270                             filePath = String.format("VENDOR/lib64/%s", dep);
271                         }
272                         depEntry = mRelContent.getEntries().get(filePath);
273                     }
274 
275                     if (depEntry == null) {
276                         // try Vendor
277                         if (mBits == 32) {
278                             filePath = String.format("VENDOR/lib/%s", dep);
279                         } else {
280                             filePath = String.format("VENDOR/lib64/%s", dep);
281                         }
282                         depEntry = mRelContent.getEntries().get(filePath);
283                     }
284                 }
285             }
286 
287             if (depEntry == null && dep.endsWith("libGLES_android.so")) {
288                 // try Vendor
289                 if (mBits == 32) {
290                     filePath = "SYSTEM/lib/egl/libGLES_android.so";
291                 } else {
292                     filePath = "SYSTEM/lib64/egl/libGLES_android.so";
293                 }
294                 depEntry = mRelContent.getEntries().get(filePath);
295             }
296 
297             if (depEntry != null) {
298                 if (mLibMap.get(targetNode) == null) {
299                     // Print Entry node once only
300                     int delta = checkDelta(depEntry);
301                     mLibMap.put(
302                             targetNode,
303                             String.format(
304                                     "%s,%d,%d", depEntry.getName(), depEntry.getSize(), delta));
305                     mEntryList.add(depEntry);
306 
307                     // Try to patch symbolic link to the target file
308                     if (depEntry.getType() == Entry.EntryType.SYMBOLIC_LINK) {
309                         filePath = depEntry.getParentFolder() + "/egl/" + depEntry.getName();
310                         Entry sDepEntry = mRelContent.getEntries().get(filePath);
311                         if (sDepEntry == null) {
312                             System.err.println(
313                                     "cannot find a target file for symbolic link: "
314                                             + depEntry.getRelativePath());
315                         } else {
316                             depEntry = sDepEntry;
317                         }
318                     }
319                     // Only visit once
320                     goFurther = true;
321                 }
322 
323                 if (goFurther) {
324                     printDep(depEntry, targetNode);
325                 }
326             } else {
327                 System.err.println("cannot find: " + filePath);
328             }
329             no++;
330         }
331         mCurLevel -= 1;
332     }
333 
334     private static final String USAGE_MESSAGE =
335             "Usage: java -cp releaseparser.jar com.android.cts.releaseparser.DepPrinter [-options]\n"
336                     + "           to compare A B builds dependency for X \n"
337                     + "Options:\n"
338                     + "\t-a A-Release.pb\t A release Content Protobuf file \n"
339                     + "\t-b B-Release.pb\t B release Content Protobuf file \n"
340                     + "\t-e Exe Name\t generates the Delta Dependency Digraph for the Execuable \n"
341                     + "\t-r \t generates RC file Delta Dependency Digraphs \n"
342                     + "\t \t without -e & -t, it will generate all Delta Dependency Digraphs \n";
343 
344     /** Get the argument or print out the usage and exit. */
printUsage()345     private static void printUsage() {
346         System.out.printf(USAGE_MESSAGE);
347         System.exit(1);
348     }
349 
350     /** Get the argument or print out the usage and exit. */
getExpectedArg(String[] args, int index)351     private static String getExpectedArg(String[] args, int index) {
352         if (index < args.length) {
353             return args[index];
354         } else {
355             printUsage();
356             return null; // Never will happen because printUsage will call exit(1)
357         }
358     }
359 
main(final String[] args)360     public static void main(final String[] args) {
361         String aPB = null;
362         String bPB = null;
363         String exeName = null;
364         boolean processRcOnly = false;
365 
366         for (int i = 0; i < args.length; i++) {
367             if (args[i].startsWith("-")) {
368                 if ("-a".equals(args[i])) {
369                     aPB = getExpectedArg(args, ++i);
370                 } else if ("-b".equals(args[i])) {
371                     bPB = getExpectedArg(args, ++i);
372                 } else if ("-e".equals(args[i])) {
373                     exeName = getExpectedArg(args, ++i);
374                 } else if ("-r".equals(args[i])) {
375                     processRcOnly = true;
376                 } else {
377                     printUsage();
378                 }
379             }
380         }
381         if (aPB == null || bPB == null) {
382             printUsage();
383         }
384 
385         try {
386             ReleaseContent aRelContent = ReleaseContent.parseFrom(new FileInputStream(aPB));
387             DepCsvPrinter depPrinter = new DepCsvPrinter(aRelContent);
388             ReleaseContent bRelContent = ReleaseContent.parseFrom(new FileInputStream(bPB));
389             String dirName =
390                     String.format("%s-vs-%s", getTitle(aRelContent), getTitle(bRelContent));
391             File dir = new File(dirName);
392             dir.mkdir();
393             if (processRcOnly) {
394                 // General RC delta digraphs
395                 depPrinter.writeRcDeltaDigraphs(bRelContent, dirName);
396             } else if (exeName == null) {
397                 // General all execuable delta digraphs
398                 depPrinter.writeDeltaDigraphs(bRelContent, dirName);
399             } else {
400                 depPrinter.writeDeltaDigraph(
401                         exeName, bRelContent, String.format("%s/%s.csv", dirName, exeName));
402             }
403 
404         } catch (Exception e) {
405             e.printStackTrace();
406             System.err.println(e);
407         }
408     }
409 }
410