xref: /aosp_15_r20/external/tensorflow/tensorflow/lite/testing/generate_examples_report.py (revision b6fb3261f9314811a0f4371741dbb8839866f948)
1# Copyright 2017 The TensorFlow Authors. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ==============================================================================
15"""Make HTML tables that report where TF and TFLite failed to convert models.
16
17This is primarily used by generate_examples.py. See it or
18`make_report_table` for more details on usage.
19"""
20import html
21import json
22import re
23
24FAILED = "FAILED"
25SUCCESS = "SUCCESS"
26NOTRUN = "NOTRUN"
27
28
29def make_report_table(fp, title, reports):
30  """Make an HTML report of the success/failure reports.
31
32  Args:
33    fp: File-like object in which to put the html.
34    title: "Title of the zip file this pertains to."
35    reports: a list of conversion attempts. (report_args, report_vals) i.e.
36      ({"shape": [1,2,3], "type": "tf.float32"},
37       {"tf": "SUCCESS", "tflite_converter": "FAILURE",
38        "tf_log": "", "tflite_converter_log": "Unsupported type."})
39  """
40  # sort reports by if TFLite converter failure and then TF failure (reversed)
41  reports.sort(key=lambda x: x[1]["tflite_converter"], reverse=False)
42  reports.sort(key=lambda x: x[1]["tf"], reverse=True)
43  def result_cell(x, row, col):
44    """Produce a cell with the condition string `x`."""
45    s = html.escape(repr(x), quote=True)
46    color = "#44ff44" if x == SUCCESS else (
47        "#ff4444" if x == FAILED else "#eeeeee")
48    handler = "ShowLog(%d, %d)" % (row, col)
49    fp.write("<td style='background-color: %s' onclick='%s'>%s</td>\n" % (
50        color, handler, s))
51
52  fp.write("""<html>
53<head>
54<title>tflite report</title>
55<style>
56body { font-family: Arial; }
57th { background-color: #555555; color: #eeeeee; }
58td { vertical-align: top; }
59td.horiz {width: 50%;}
60pre { white-space: pre-wrap; word-break: keep-all; }
61table {width: 100%;}
62</style>
63</head>
64""")
65  # Write the log data to a javascript variable and also make a function
66  # in javascript to show the log when an item is clicked.
67  fp.write("<script> \n")
68  fp.write("""
69function ShowLog(row, col) {
70
71var log = document.getElementById("log");
72log.innerHTML = "<pre>" + data[row][col]  + "</pre>";
73}
74""")
75  fp.write("var data = \n")
76  logs = json.dumps([[escape_and_normalize(x[1]["tf_log"]),
77                      escape_and_normalize(x[1]["tflite_converter_log"])
78                     ] for x in reports])
79  fp.write(logs)
80  fp.write(";</script>\n")
81
82  # Write the main table and use onclick on the items that have log items.
83  fp.write("""
84<body>
85<h1>TensorFlow Lite Conversion</h1>
86<h2>%s</h2>
87""" % title)
88
89  # Get a list of keys that are in any of the records.
90  param_keys = {}
91  for params, _ in reports:
92    for k in params.keys():
93      param_keys[k] = True
94
95  fp.write("<table>\n")
96  fp.write("<tr><td class='horiz'>\n")
97  fp.write("<div style='height:1000px; overflow:auto'>\n")
98  fp.write("<table>\n")
99  fp.write("<tr>\n")
100  for p in param_keys:
101    fp.write("<th>%s</th>\n" % html.escape(p, quote=True))
102  fp.write("<th>TensorFlow</th>\n")
103  fp.write("<th>TensorFlow Lite Converter</th>\n")
104  fp.write("</tr>\n")
105  for idx, (params, vals) in enumerate(reports):
106    fp.write("<tr>\n")
107    for p in param_keys:
108      fp.write("  <td>%s</td>\n" %
109               html.escape(repr(params.get(p, None)), quote=True))
110
111    result_cell(vals["tf"], idx, 0)
112    result_cell(vals["tflite_converter"], idx, 1)
113    fp.write("</tr>\n")
114  fp.write("</table>\n")
115  fp.write("</div>\n")
116  fp.write("</td>\n")
117  fp.write("<td class='horiz' id='log'></td></tr>\n")
118  fp.write("</table>\n")
119  fp.write("<script>\n")
120  fp.write("</script>\n")
121  fp.write("""
122    </body>
123    </html>
124    """)
125
126
127def escape_and_normalize(log):
128  # These logs contain paths like /tmp/tmpgmypg3xa that are inconsistent between
129  # builds. This replaces these inconsistent paths with a consistent placeholder
130  # so the output is deterministic.
131  log = re.sub(r"/tmp/[^ ]+ ", "/NORMALIZED_TMP_FILE_PATH ", log)
132  log = re.sub(r"/build/work/[^/]+", "/NORMALIZED_BUILD_PATH", log)
133  return html.escape(log, quote=True)
134