Download utils/cpg_utils.py from Android12138/FlowResampler: direct link, hf CLI and curl.
- Browser
- Download file 7.77 kB
-
https://huggingface.co/Android12138/FlowResampler/resolve/main/utils/cpg_utils.py
- Command line
-
hf download hf://Android12138/FlowResampler/utils/cpg_utils.py
-
curl -L -o cpg_utils.py https://huggingface.co/Android12138/FlowResampler/resolve/main/utils/cpg_utils.py
7.77 kB
| import os | |
| import re | |
| import json | |
| import shutil | |
| import argparse | |
| import subprocess | |
| from tqdm import tqdm | |
| from multiprocessing import Process, current_process | |
| import pydot | |
| import networkx as nx | |
| from networkx.readwrite import json_graph | |
| from utils.json_utils import save_jsonl, load_jsonl | |
| # Global configuration for file paths and directories | |
| PROJECT_ROOT = os.getcwd() | |
| OUTPUT_BASE_DIR = f"{PROJECT_ROOT}/datasets/processed/primevul" | |
| OUTPUT_CHUNKS_DIR = f"{OUTPUT_BASE_DIR}/data_chunks/cpg" | |
| OUTPUT_GRAPHS_DIR = f"{OUTPUT_BASE_DIR}/data_graphs/cpg" | |
| # Joern tool paths | |
| JOERN_PARSE_PATH = os.path.join(PROJECT_ROOT, "utils/joern-cli/joern-parse") | |
| JOERN_EXPORT_PATH = os.path.join(PROJECT_ROOT, "utils/joern-cli/joern-export") | |
| def process_data(chunk_index): | |
| # Get the process ID for naming the output files and temporary directories | |
| process_id = current_process().name | |
| # Read the data samples | |
| data_samples = [] | |
| with open(input_jsonl_path, "r") as file: | |
| for i, line in enumerate(file): | |
| if i < chunk_index[0]: # Skip lines before the start index | |
| continue | |
| if i >= chunk_index[1]: # Stop reading after the end index | |
| break | |
| try: # Load the JSON object within chunk_index | |
| data_samples.append(json.loads(line.strip())) | |
| except json.JSONDecodeError as e: | |
| print(f"Error decoding JSON: {e}") | |
| # Create a list to store the graphs | |
| raw_graphs = [] | |
| processed_graphs = [] | |
| # Initialize the temporary directories and create them if they do not exist | |
| temp_dir = f"{OUTPUT_BASE_DIR}/temporary/{process_id}" # temp_dir is the directory to store temporary files (including temp.c, cpg.bin, and temp folder) | |
| os.makedirs(temp_dir, exist_ok=True) | |
| # Set the current working directory to the temp_dir so joern-parse and joern-export can put the temp files in temp_dir | |
| os.chdir(temp_dir) | |
| # Define the variable used by joern-parse to process and save the output into temp_dir | |
| temp_file = os.path.join(temp_dir, "temp.c") # define the path to the directory of temp.c file | |
| graph_type = "cpg" # the type of graph to export, can be "cpg", "cfg", or "ast" | |
| representation = f"--repr={graph_type}" | |
| output_dir = f"--out=temp" | |
| output_format = "--format=dot" | |
| for data_sample in tqdm(data_samples, desc=f"processing chunk {process_id}"): | |
| # Write the C code to a temp.c file for each data sample | |
| with open(temp_file, "w") as file: | |
| file.write(data_sample["func"]) | |
| # Run the command to parse the input file into cpg.bin under the home directory | |
| parse_result = subprocess.run([JOERN_PARSE_PATH, temp_file], stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
| # Run the command to export the parsed graph into a .dot file | |
| export_result = subprocess.run([JOERN_EXPORT_PATH, representation, output_dir, output_format], stdout=subprocess.PIPE, stderr=subprocess.PIPE) | |
| # Load the .dot file using pydot | |
| (graph,) = pydot.graph_from_dot_file(os.path.join(temp_dir, "temp/temp.c/_global_.dot")) | |
| # Convert to a NetworkX graph | |
| nx_graph = nx.nx_pydot.from_pydot(graph) | |
| # Save the raw graph (with all attributes) to the list of raw graphs | |
| raw_graphs.append(json_graph.node_link_data(nx_graph)) | |
| # Iterate over each node in the graph and keep only the "CODE" and "label" attributes | |
| for node in nx_graph.nodes: | |
| # Get the attributes of the current node | |
| attributes = nx_graph.nodes[node] | |
| # Only keep "CODE" and "label", remove others | |
| for attr in list(attributes): # Convert to list to avoid changing dict size during iteration | |
| if attr not in ["CODE", "label"]: | |
| del nx_graph.nodes[node][attr] | |
| # Iterate over all edges and keep only the "label" attribute | |
| for u, v, attr in nx_graph.edges(data=True): | |
| label_value = attr.get("label") # Get the "label" attribute if it exists | |
| # Clear all attributes and re-add the "label" if it exists | |
| attr.clear() | |
| if label_value is not None: | |
| attr["label"] = label_value | |
| # Append the processed graph to the list of processed graphs | |
| processed_graphs.append(json_graph.node_link_data(nx_graph)) | |
| # Delete the temp.c file, cpg.bin, and temp folder under the temp_dir | |
| os.remove(temp_file) | |
| os.remove(os.path.join(temp_dir, "cpg.bin")) | |
| shutil.rmtree(os.path.join(temp_dir, "temp")) | |
| # Save the results | |
| os.makedirs(os.path.join(OUTPUT_CHUNKS_DIR, "raw"), exist_ok=True) | |
| os.makedirs(os.path.join(OUTPUT_CHUNKS_DIR, "processed"), exist_ok=True) | |
| save_jsonl(os.path.join(OUTPUT_CHUNKS_DIR, f"raw/{process_id}.jsonl"), raw_graphs) | |
| save_jsonl(os.path.join(OUTPUT_CHUNKS_DIR, f"processed/{process_id}.jsonl"), processed_graphs) | |
| if __name__ == "__main__": | |
| # Create an argument parser | |
| parser = argparse.ArgumentParser(description="Run different sets of experiments") | |
| parser.add_argument( | |
| "--file_name", type=str, | |
| choices=["primevul_test.jsonl", "primevul_train.jsonl", "primevul_valid.jsonl"], | |
| help="The file name of the dataset to process.", | |
| ) | |
| # Parse the arguments | |
| args = parser.parse_args() | |
| # Define the input and output file paths based on the provided file name | |
| input_jsonl_path = os.path.join(PROJECT_ROOT, f"datasets/raw/primevul/{args.file_name}") | |
| # Calculate the number of data samples | |
| with open(input_jsonl_path, "r") as file: | |
| number_of_data_samples = sum(1 for _ in file) | |
| # Split the data_samples into chunks | |
| num_chunks = os.cpu_count() | |
| chunk_size = number_of_data_samples // num_chunks | |
| data_chunks_index = [[i, min(i + chunk_size, number_of_data_samples)] for i in range(0, number_of_data_samples, chunk_size)] | |
| # Create and start processes for each chunk | |
| processes = [] | |
| for i, chunk_index in enumerate(data_chunks_index): | |
| p = Process(target=process_data, args=(chunk_index,)) | |
| processes.append(p) | |
| p.start() | |
| # Wait for all processes to finish | |
| for p in processes: | |
| p.join() | |
| print("Processing completed!") | |
| # Merge the data chunks into memory lists, then save to avoid overwriting | |
| for graph_type in ["raw", "processed"]: | |
| # Initialize an empty list to hold the merged data from all chunks | |
| merged_data = [] | |
| chunk_dir = os.path.join(OUTPUT_CHUNKS_DIR, graph_type) | |
| chunk_files = [f for f in os.listdir(chunk_dir) if f.endswith('.jsonl')] | |
| chunk_files.sort(key=lambda x: int(re.search(r'\d+', x).group()) if re.search(r'\d+', x) else 0) | |
| # Load each chunk file and extend the merged_data list | |
| for chunk_file in tqdm(chunk_files, desc=f"Loading {graph_type} chunks"): | |
| chunk_file_path = os.path.join(chunk_dir, chunk_file) | |
| merged_data.extend(load_jsonl(chunk_file_path)) | |
| # Save the fully extended list to distinct files | |
| output_jsonl_path = os.path.join(OUTPUT_GRAPHS_DIR, f"{graph_type}/{args.file_name}") | |
| os.makedirs(os.path.dirname(output_jsonl_path), exist_ok=True) | |
| save_jsonl(output_jsonl_path, merged_data) | |
| print(f"Merged {graph_type} saved to: {output_jsonl_path}") | |
| # Delete the temporary directory after merging | |
| if os.path.exists(OUTPUT_CHUNKS_DIR): | |
| shutil.rmtree(OUTPUT_CHUNKS_DIR) | |
| shutil.rmtree(os.path.join(OUTPUT_BASE_DIR, "temporary")) | |
| print(f"Deleted temporary directory.") | |