mirror of https://github.com/mongodb/mongo
389 lines
15 KiB
Python
389 lines
15 KiB
Python
#!/usr/bin/env python3
|
||
#
|
||
# Copyright 2022 MongoDB Inc.
|
||
#
|
||
# Permission is hereby granted, free of charge, to any person obtaining
|
||
# a copy of this software and associated documentation files (the
|
||
# "Software"), to deal in the Software without restriction, including
|
||
# without limitation the rights to use, copy, modify, merge, publish,
|
||
# distribute, sublicense, and/or sell copies of the Software, and to
|
||
# permit persons to whom the Software is furnished to do so, subject to
|
||
# the following conditions:
|
||
#
|
||
# The above copyright notice and this permission notice shall be included
|
||
# in all copies or substantial portions of the Software.
|
||
#
|
||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
|
||
# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
|
||
# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||
#
|
||
"""Test graphs for the graph visualizer and analyzer."""
|
||
|
||
import argparse
|
||
import json
|
||
import os
|
||
|
||
import networkx
|
||
from libdeps.graph import EdgeProps, LibdepsGraph, NodeProps
|
||
|
||
|
||
def get_args():
|
||
"""Create the argparse and return passed args."""
|
||
|
||
parser = argparse.ArgumentParser()
|
||
|
||
parser.add_argument('--graph-output-dir', type=str, action='store',
|
||
help="Directory test graphml files will be saved.",
|
||
default="build/opt/libdeps")
|
||
|
||
parser.add_argument('--generate-big-graphs', action='store_true',
|
||
help="Makes graphs which are large for testing scale.", default=False)
|
||
|
||
return parser.parse_args()
|
||
|
||
|
||
def add_node(graph, node, builder):
|
||
"""Add a node to the graph."""
|
||
|
||
graph.add_nodes_from([(node, {NodeProps.bin_type.name: builder})])
|
||
|
||
|
||
def add_edge(graph, from_node, to_node, **kwargs):
|
||
"""Add an edge to the graph."""
|
||
|
||
edge_props = {
|
||
EdgeProps.direct.name: kwargs[EdgeProps.direct.name],
|
||
EdgeProps.visibility.name: int(kwargs[EdgeProps.visibility.name]),
|
||
}
|
||
if kwargs.get('symbols'):
|
||
edge_props[EdgeProps.symbols.name] = kwargs.get('symbols')
|
||
|
||
graph.add_edges_from([(from_node, to_node, edge_props)])
|
||
|
||
|
||
def get_big_graph(int_id):
|
||
"""Generate a big graph."""
|
||
|
||
graph = LibdepsGraph()
|
||
graph.graph['build_dir'] = '.'
|
||
graph.graph['graph_schema_version'] = 2
|
||
graph.graph['deptypes'] = json.dumps({
|
||
"Global": 0,
|
||
"Public": 1,
|
||
"Private": 2,
|
||
"Interface": 3,
|
||
})
|
||
graph.graph['git_hash'] = f'BIG{int_id.zfill(4)}'
|
||
num_nodes = 200
|
||
for i in range(num_nodes):
|
||
add_node(graph, f'lib{i}.so', 'SharedLibrary')
|
||
for j in range(num_nodes - i):
|
||
add_edge(graph, f'lib{i}.so', f'lib{j}.so', direct=True,
|
||
visibility=graph.get_deptype('Public'), symbols='\n'.join(
|
||
[f"RandomString{i+j}" * 100 for i in range(10)]))
|
||
return graph
|
||
|
||
|
||
def get_double_diamond_mock_graph():
|
||
"""Construct a mock graph which covers a double diamond structure."""
|
||
|
||
graph = LibdepsGraph()
|
||
graph.graph['build_dir'] = '.'
|
||
graph.graph['graph_schema_version'] = 2
|
||
graph.graph['deptypes'] = json.dumps({
|
||
"Global": 0,
|
||
"Public": 1,
|
||
"Private": 2,
|
||
"Interface": 3,
|
||
})
|
||
graph.graph['git_hash'] = 'TEST001'
|
||
|
||
# builds a graph of mostly public edges that looks like this:
|
||
#
|
||
#
|
||
# /lib3.so /lib7.so
|
||
# | \ | \
|
||
# <-lib1.so--lib2.so lib5.so--lib6.so lib9.so
|
||
# | / | /
|
||
# \lib4.so \lib8.so
|
||
#
|
||
|
||
add_node(graph, 'lib1.so', 'SharedLibrary')
|
||
add_node(graph, 'lib2.so', 'SharedLibrary')
|
||
add_node(graph, 'lib3.so', 'SharedLibrary')
|
||
add_node(graph, 'lib4.so', 'SharedLibrary')
|
||
add_node(graph, 'lib5.so', 'SharedLibrary')
|
||
add_node(graph, 'lib6.so', 'SharedLibrary')
|
||
add_node(graph, 'lib7.so', 'SharedLibrary')
|
||
add_node(graph, 'lib8.so', 'SharedLibrary')
|
||
add_node(graph, 'lib9.so', 'SharedLibrary')
|
||
|
||
add_edge(graph, 'lib1.so', 'lib2.so', direct=True, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib2.so', 'lib3.so', direct=True, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib2.so', 'lib4.so', direct=True, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib3.so', 'lib5.so', direct=True, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib4.so', 'lib5.so', direct=True, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib5.so', 'lib6.so', direct=True, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib6.so', 'lib7.so', direct=True, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib6.so', 'lib8.so', direct=True, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib7.so', 'lib9.so', direct=True, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib8.so', 'lib9.so', direct=True, visibility=graph.get_deptype('Public'))
|
||
|
||
# trans for 3 and 4
|
||
add_edge(graph, 'lib1.so', 'lib3.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib1.so', 'lib4.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
|
||
# trans for 5
|
||
add_edge(graph, 'lib1.so', 'lib5.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib2.so', 'lib5.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
|
||
# trans for 6
|
||
add_edge(graph, 'lib1.so', 'lib6.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib2.so', 'lib6.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib3.so', 'lib6.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib4.so', 'lib6.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
|
||
# trans for 7
|
||
add_edge(graph, 'lib1.so', 'lib7.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib2.so', 'lib7.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib3.so', 'lib7.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib4.so', 'lib7.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib5.so', 'lib7.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
|
||
# trans for 8
|
||
add_edge(graph, 'lib1.so', 'lib8.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib2.so', 'lib8.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib3.so', 'lib8.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib4.so', 'lib8.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib5.so', 'lib8.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
|
||
# trans for 9
|
||
add_edge(graph, 'lib1.so', 'lib9.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib2.so', 'lib9.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib3.so', 'lib9.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib4.so', 'lib9.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib5.so', 'lib9.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib6.so', 'lib9.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
|
||
return graph
|
||
|
||
|
||
def get_basic_mock_graph():
|
||
"""Construct a mock graph which covers most cases and is easy to understand."""
|
||
|
||
graph = LibdepsGraph()
|
||
graph.graph['build_dir'] = '.'
|
||
graph.graph['graph_schema_version'] = 2
|
||
graph.graph['deptypes'] = json.dumps({
|
||
"Global": 0,
|
||
"Public": 1,
|
||
"Private": 2,
|
||
"Interface": 3,
|
||
})
|
||
graph.graph['git_hash'] = 'TEST002'
|
||
|
||
# builds a graph of mostly public edges:
|
||
#
|
||
# /-lib5.so
|
||
# /lib3.so
|
||
# | \-lib6.so
|
||
# <-lib1.so--lib2.so
|
||
# | /-lib5.so (private)
|
||
# \lib4.so
|
||
# \-lib6.so
|
||
|
||
# nodes
|
||
add_node(graph, 'lib1.so', 'SharedLibrary')
|
||
add_node(graph, 'lib2.so', 'SharedLibrary')
|
||
add_node(graph, 'lib3.so', 'SharedLibrary')
|
||
add_node(graph, 'lib4.so', 'SharedLibrary')
|
||
add_node(graph, 'lib5.so', 'SharedLibrary')
|
||
add_node(graph, 'lib6.so', 'SharedLibrary')
|
||
|
||
# direct edges
|
||
add_edge(graph, 'lib1.so', 'lib2.so', direct=True, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib2.so', 'lib3.so', direct=True, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib2.so', 'lib4.so', direct=True, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib4.so', 'lib6.so', direct=True, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib3.so', 'lib5.so', direct=True, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib3.so', 'lib6.so', direct=True, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib4.so', 'lib5.so', direct=True, visibility=graph.get_deptype('Private'))
|
||
|
||
# trans for 3
|
||
add_edge(graph, 'lib1.so', 'lib3.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
|
||
# trans for 4
|
||
add_edge(graph, 'lib1.so', 'lib4.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
|
||
# trans for 5
|
||
add_edge(graph, 'lib2.so', 'lib5.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib1.so', 'lib5.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
|
||
# trans for 6
|
||
add_edge(graph, 'lib2.so', 'lib6.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'lib1.so', 'lib6.so', direct=False, visibility=graph.get_deptype('Public'))
|
||
|
||
return graph
|
||
|
||
|
||
def get_basic_mock_directory_graph():
|
||
"""Construct a mock graph which covers most cases and is easy to understand."""
|
||
|
||
graph = LibdepsGraph()
|
||
graph.graph['build_dir'] = '.'
|
||
graph.graph['graph_schema_version'] = 2
|
||
graph.graph['deptypes'] = json.dumps({
|
||
"Global": 0,
|
||
"Public": 1,
|
||
"Private": 2,
|
||
"Interface": 3,
|
||
})
|
||
graph.graph['git_hash'] = 'TEST003'
|
||
|
||
# builds a graph of mostly public edges:
|
||
#
|
||
# /-lib5.so
|
||
# /lib3
|
||
# | \-lib6.so
|
||
# <-lib1.so--lib2
|
||
# | /-lib5.so (private)
|
||
# \lib4.so
|
||
# \-lib6.so
|
||
|
||
# nodes
|
||
add_node(graph, 'dir1/lib1.so', 'SharedLibrary')
|
||
add_node(graph, 'dir1/sub1/lib2', 'Program')
|
||
add_node(graph, 'dir1/sub1/lib3', 'Program')
|
||
add_node(graph, 'dir1/sub2/lib4.so', 'SharedLibrary')
|
||
add_node(graph, 'dir2/lib5.so', 'SharedLibrary')
|
||
add_node(graph, 'dir2/lib6.so', 'SharedLibrary')
|
||
|
||
# direct edges
|
||
add_edge(graph, 'dir1/lib1.so', 'dir1/sub1/lib2', direct=True,
|
||
visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'dir1/sub1/lib2', 'dir1/sub1/lib3', direct=True,
|
||
visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'dir1/sub1/lib2', 'dir1/sub2/lib4.so', direct=True,
|
||
visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'dir1/sub2/lib4.so', 'dir2/lib6.so', direct=True,
|
||
visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'dir1/sub1/lib3', 'dir2/lib5.so', direct=True,
|
||
visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'dir1/sub1/lib3', 'dir2/lib6.so', direct=True,
|
||
visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'dir1/sub2/lib4.so', 'dir2/lib5.so', direct=True,
|
||
visibility=graph.get_deptype('Private'))
|
||
|
||
# trans for 3
|
||
add_edge(graph, 'dir1/lib1.so', 'dir1/sub1/lib3', direct=False,
|
||
visibility=graph.get_deptype('Public'))
|
||
|
||
# trans for 4
|
||
add_edge(graph, 'dir1/lib1.so', 'dir1/sub2/lib4.so', direct=False,
|
||
visibility=graph.get_deptype('Public'))
|
||
|
||
# trans for 5
|
||
add_edge(graph, 'dir1/sub1/lib2', 'dir2/lib5.so', direct=False,
|
||
visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'dir1/lib1.so', 'dir2/lib5.so', direct=False,
|
||
visibility=graph.get_deptype('Public'))
|
||
|
||
# trans for 6
|
||
add_edge(graph, 'dir1/sub1/lib2', 'dir2/lib6.so', direct=False,
|
||
visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'dir1/lib1.so', 'dir2/lib6.so', direct=False,
|
||
visibility=graph.get_deptype('Public'))
|
||
|
||
return graph
|
||
|
||
|
||
def get_simple_directory_graph():
|
||
"""Construct a mock graph which covers most cases and is easy to understand."""
|
||
|
||
graph = LibdepsGraph()
|
||
graph.graph['build_dir'] = '.'
|
||
graph.graph['graph_schema_version'] = 2
|
||
graph.graph['deptypes'] = json.dumps({
|
||
"Global": 0,
|
||
"Public": 1,
|
||
"Private": 2,
|
||
"Interface": 3,
|
||
})
|
||
graph.graph['git_hash'] = 'TEST004'
|
||
|
||
# lib2.so <- lib4.so
|
||
# /∧ \∨
|
||
# lib1.so prog1 <- lib5.so
|
||
# \∨ /∧
|
||
# lib3.so -> prog2
|
||
|
||
# nodes
|
||
add_node(graph, 'mongo/base/lib1.so', 'SharedLibrary')
|
||
add_node(graph, 'mongo/base/lib2.so', 'SharedLibrary')
|
||
add_node(graph, 'mongo/db/lib3.so', 'SharedLibrary')
|
||
add_node(graph, 'third_party/lib4.so', 'SharedLibrary')
|
||
add_node(graph, 'third_party/lib5.so', 'SharedLibrary')
|
||
add_node(graph, 'mongo/base/prog1', 'Program')
|
||
add_node(graph, 'mongo/db/prog2', 'Program')
|
||
|
||
# direct edges
|
||
add_edge(graph, 'mongo/base/lib1.so', 'mongo/base/lib2.so', direct=True,
|
||
visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'mongo/base/lib1.so', 'mongo/db/lib3.so', direct=True,
|
||
visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'mongo/base/lib2.so', 'mongo/base/prog1', direct=True,
|
||
visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'mongo/db/lib3.so', 'mongo/base/prog1', direct=True,
|
||
visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'mongo/db/lib3.so', 'mongo/db/prog2', direct=True,
|
||
visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'third_party/lib4.so', 'mongo/base/lib2.so', direct=True,
|
||
visibility=graph.get_deptype('Public'))
|
||
add_edge(graph, 'third_party/lib5.so', 'mongo/base/prog1', direct=True,
|
||
visibility=graph.get_deptype('Public'))
|
||
|
||
return graph
|
||
|
||
|
||
def save_graph_file(graph, output_dir):
|
||
"""Save a graph locally as a .graphml."""
|
||
|
||
filename = output_dir + "/libdeps_" + graph.graph['git_hash'] + ".graphml"
|
||
networkx.write_graphml(graph, filename, named_key_ids=True)
|
||
|
||
|
||
def main():
|
||
"""Generate and save the test graphs as .graphml files."""
|
||
|
||
args = get_args()
|
||
output_dir = args.graph_output_dir
|
||
|
||
os.makedirs(output_dir, exist_ok=True)
|
||
|
||
graph = get_double_diamond_mock_graph()
|
||
save_graph_file(graph, output_dir)
|
||
|
||
graph = get_basic_mock_graph()
|
||
save_graph_file(graph, output_dir)
|
||
|
||
graph = get_basic_mock_directory_graph()
|
||
save_graph_file(graph, output_dir)
|
||
|
||
graph = get_simple_directory_graph()
|
||
save_graph_file(graph, output_dir)
|
||
|
||
if args.generate_big_graphs:
|
||
graph = get_big_graph("0")
|
||
for i in range(1, 30):
|
||
print(f"generating big graph {i}...")
|
||
graph.graph['git_hash'] = f'BIG{str(i).zfill(4)}'
|
||
save_graph_file(graph, output_dir)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|