added interdependence graph generation argument
This commit is contained in:
parent
6b5ed45943
commit
b5ec3c3329
4 changed files with 229 additions and 44 deletions
|
@ -7,7 +7,6 @@
|
|||
package buildtool
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -106,6 +105,32 @@ func parseMakeOutput(output string) string {
|
|||
return sb.String()
|
||||
}
|
||||
|
||||
// addConfigFiles adds user-provided configuration files to the app unikernel folder.
|
||||
func addConfigFiles(configFiles []string, selectedFiles *[]string, includeFolder,
|
||||
appFolder string) {
|
||||
|
||||
for _, configFilePath := range configFiles {
|
||||
configFile := filepath.Base(configFilePath)
|
||||
fileExt := filepath.Ext(configFile)
|
||||
|
||||
// Copy config file
|
||||
if fileExt == ".h" || fileExt == ".hpp" || fileExt == ".hcc" {
|
||||
if err := u.CopyFileContents(configFilePath, includeFolder+configFile); err != nil {
|
||||
u.PrintErr(err)
|
||||
}
|
||||
} else if fileExt == ".c" || fileExt == ".cpp" || fileExt == ".cc" {
|
||||
if err := u.CopyFileContents(configFilePath, appFolder+configFile); err != nil {
|
||||
u.PrintErr(err)
|
||||
}
|
||||
|
||||
// Add Makefile.uk entry
|
||||
*selectedFiles = append(*selectedFiles, configFile)
|
||||
} else {
|
||||
u.PrintWarning("Unsupported extension for file: " + configFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------Run-------------------------------------
|
||||
|
||||
// RunBuildTool runs the automatic build tool to build a unikernel of a
|
||||
|
@ -123,8 +148,6 @@ func RunBuildTool(homeDir string, data *u.Data) {
|
|||
u.PrintErr(err)
|
||||
}
|
||||
|
||||
fmt.Println(args)
|
||||
fmt.Println(*args.StringListArg[configArg])
|
||||
// Get program Name
|
||||
programName := *args.StringArg[programArg]
|
||||
|
||||
|
@ -216,25 +239,7 @@ func RunBuildTool(homeDir string, data *u.Data) {
|
|||
}
|
||||
|
||||
// Move config files to Unikraft folder
|
||||
configArg := *args.StringListArg[configArg]
|
||||
for _, configFilePath := range configArg {
|
||||
pathSplit := strings.Split(configFilePath, "/")
|
||||
file := pathSplit[len(pathSplit)-1]
|
||||
fileSplit := strings.Split(file, ".")
|
||||
ext := fileSplit[len(fileSplit)-1]
|
||||
|
||||
if ext == "h" || ext == "hpp" || ext == "hcc" {
|
||||
if err = u.CopyFileContents(configFilePath, *includeFolder+file); err != nil {
|
||||
u.PrintErr(err)
|
||||
}
|
||||
} else if ext == "c" || ext == "cpp" || ext == "cc" {
|
||||
if err = u.CopyFileContents(configFilePath, appFolder+file); err != nil {
|
||||
u.PrintErr(err)
|
||||
}
|
||||
} else {
|
||||
u.PrintWarning("Unsupported extension for file: " + file)
|
||||
}
|
||||
}
|
||||
addConfigFiles(*args.StringListArg[configArg], &selectedFiles, *includeFolder, appFolder)
|
||||
|
||||
// Match micro-libs
|
||||
matchedLibs, externalLibs, err := matchLibs(unikraftPath+"lib"+u.SEP, data)
|
||||
|
@ -242,8 +247,6 @@ func RunBuildTool(homeDir string, data *u.Data) {
|
|||
u.PrintErr(err)
|
||||
}
|
||||
|
||||
fmt.Println("\nPREFINAL\n")
|
||||
fmt.Println(matchedLibs)
|
||||
// Clone the external git repositories
|
||||
cloneLibsFolders(workspacePath, matchedLibs, externalLibs)
|
||||
|
||||
|
@ -257,8 +260,6 @@ func RunBuildTool(homeDir string, data *u.Data) {
|
|||
u.PrintOk("Match lib: " + lib)
|
||||
}
|
||||
|
||||
fmt.Println("\nFINAL\n")
|
||||
fmt.Println(matchedLibs)
|
||||
// Clone the external git repositories (if changed)
|
||||
cloneLibsFolders(workspacePath, matchedLibs, externalLibs)
|
||||
|
||||
|
|
|
@ -7,9 +7,10 @@
|
|||
package dependtool
|
||||
|
||||
import (
|
||||
"github.com/akamensky/argparse"
|
||||
"os"
|
||||
u "tools/srcs/common"
|
||||
|
||||
"github.com/akamensky/argparse"
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -22,6 +23,7 @@ const (
|
|||
fullDepsArg = "fullDeps"
|
||||
fullStaticAnalysis = "fullStaticAnalysis"
|
||||
typeAnalysis = "typeAnalysis"
|
||||
interdependArg = "interdepend"
|
||||
)
|
||||
|
||||
// parseLocalArguments parses arguments of the application.
|
||||
|
@ -53,6 +55,9 @@ func parseLocalArguments(p *argparse.Parser, args *u.Arguments) error {
|
|||
args.InitArgParse(p, args, u.INT, "", typeAnalysis,
|
||||
&argparse.Options{Required: false, Default: 0,
|
||||
Help: "Kind of analysis (0: both; 1: static; 2: dynamic)"})
|
||||
args.InitArgParse(p, args, u.BOOL, "i", interdependArg,
|
||||
&argparse.Options{Required: false, Default: false,
|
||||
Help: "Create the source files interdependence graph"})
|
||||
|
||||
return u.ParserWrapper(p, os.Args)
|
||||
}
|
||||
|
|
|
@ -4,6 +4,8 @@ import (
|
|||
"debug/elf"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
u "tools/srcs/common"
|
||||
|
@ -11,6 +13,110 @@ import (
|
|||
"github.com/fatih/color"
|
||||
)
|
||||
|
||||
// sourceFileIncludesAnalysis collects all the include directives from a C/C++ source file.
|
||||
//
|
||||
// It returns a slice containing the found header names.
|
||||
func sourceFileIncludesAnalysis(sourceFile string) []string {
|
||||
|
||||
var fileIncludes []string
|
||||
|
||||
fileLines, err := u.ReadLinesFile(sourceFile)
|
||||
if err != nil {
|
||||
u.PrintErr(err)
|
||||
}
|
||||
|
||||
for _, line := range fileLines {
|
||||
if strings.Contains(line, "#include") {
|
||||
line = strings.ReplaceAll(line, " ", "")
|
||||
line := strings.Split(line, "#include")[1]
|
||||
if strings.HasPrefix(line, "\"") {
|
||||
fileIncludes = append(fileIncludes, line[1:strings.Index(line[1:], "\"")+1])
|
||||
} else if strings.HasPrefix(line, "<") {
|
||||
fileIncludes = append(fileIncludes, line[1:strings.Index(line[1:], ">")+1])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fileIncludes
|
||||
}
|
||||
|
||||
// gccSourceFileIncludesAnalysis collects all the include directives from a C/C++ source file using
|
||||
// the gcc preprocessor.
|
||||
//
|
||||
// It returns a slice containing the found header names.
|
||||
func gccSourceFileIncludesAnalysis(sourceFile, tmpFolder string) []string {
|
||||
|
||||
var fileIncludes []string
|
||||
|
||||
outputStr, _ := ExecuteCommand("gcc", []string{"-E", sourceFile, "-I", tmpFolder})
|
||||
outputSlice := strings.Split(outputStr, "\n")
|
||||
|
||||
for _, line := range outputSlice {
|
||||
|
||||
// Only interested in headers not coming from the standard library
|
||||
if strings.Contains(line, "\""+tmpFolder) {
|
||||
line = strings.Split(line, "\""+tmpFolder)[1]
|
||||
includeDirective := line[0:strings.Index(line[0:], "\"")]
|
||||
if !u.Contains(fileIncludes, includeDirective) {
|
||||
fileIncludes = append(fileIncludes, includeDirective)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fileIncludes
|
||||
}
|
||||
|
||||
// runInterdependAnalyser collects all the included headers names (i.e., dependencies) from each
|
||||
// C/C++ source file of a program and builds an interdependence graph (dot file) between all these
|
||||
// source files.
|
||||
func runInterdependAnalyser(programPath, programName, outFolder string) {
|
||||
|
||||
// Find all program source files
|
||||
sourceFiles, err := findSourcesFiles(getProgramFolder(programPath))
|
||||
if err != nil {
|
||||
u.PrintErr(err)
|
||||
}
|
||||
|
||||
// Create a temporary folder and copy all source files into it for use with the gcc
|
||||
// preprocessor
|
||||
tmpFolder := "tmp/"
|
||||
_, err = u.CreateFolder(tmpFolder)
|
||||
if err != nil {
|
||||
u.PrintErr(err)
|
||||
}
|
||||
var tmpFiles []string
|
||||
for _, sourceFilePath := range sourceFiles {
|
||||
if err := u.CopyFileContents(sourceFilePath,
|
||||
tmpFolder+filepath.Base(sourceFilePath)); err != nil {
|
||||
u.PrintErr(err)
|
||||
}
|
||||
tmpFiles = append(tmpFiles, tmpFolder+filepath.Base(sourceFilePath))
|
||||
}
|
||||
|
||||
// Analyse source files include directives and collect header names. Source files are first
|
||||
// analysed "by hand" to get all their include directives and then by gcc to make sure to avoid
|
||||
// directives that are commented or subjected to a macro.
|
||||
interdependMap := make(map[string][]string)
|
||||
for _, tmpFile := range tmpFiles {
|
||||
interdependMap[filepath.Base(tmpFile)] = make([]string, 0)
|
||||
analysis := sourceFileIncludesAnalysis(tmpFile)
|
||||
gccAnalysis := gccSourceFileIncludesAnalysis(tmpFile, tmpFolder)
|
||||
for _, includeDirective := range gccAnalysis {
|
||||
if u.Contains(analysis, includeDirective) {
|
||||
interdependMap[filepath.Base(tmpFile)] =
|
||||
append(interdependMap[filepath.Base(tmpFile)], includeDirective)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create dot file
|
||||
u.GenerateGraph(programName, outFolder+programName, interdependMap, nil)
|
||||
|
||||
// Remove tmp folder
|
||||
u.PrintInfo("Remove folder " + tmpFolder)
|
||||
_ = os.RemoveAll(tmpFolder)
|
||||
}
|
||||
|
||||
// RunAnalyserTool allows to run the dependency analyser tool.
|
||||
func RunAnalyserTool(homeDir string, data *u.Data) {
|
||||
|
||||
|
@ -95,6 +201,11 @@ func RunAnalyserTool(homeDir string, data *u.Data) {
|
|||
if *args.BoolArg[fullDepsArg] {
|
||||
saveGraph(programName, outFolder, data)
|
||||
}
|
||||
|
||||
// Create source files interdependence graph if interdependence option is set
|
||||
if *args.BoolArg[interdependArg] {
|
||||
runInterdependAnalyser(programPath, programName, outFolder)
|
||||
}
|
||||
}
|
||||
|
||||
// displayProgramDetails display various information such path, background, ...
|
||||
|
@ -157,8 +268,8 @@ func checkElf(programPath *string) (*elf.File, bool) {
|
|||
}
|
||||
|
||||
// runStaticAnalyser runs the static analyser
|
||||
func runStaticAnalyser(elfFile *elf.File, isDynamic, isLinux bool, args *u.Arguments, programName, programPath,
|
||||
outFolder string, data *u.Data) {
|
||||
func runStaticAnalyser(elfFile *elf.File, isDynamic, isLinux bool, args *u.Arguments, programName,
|
||||
programPath, outFolder string, data *u.Data) {
|
||||
|
||||
staticAnalyser(elfFile, isDynamic, isLinux, *args, data, programPath)
|
||||
|
||||
|
@ -228,3 +339,64 @@ func saveGraph(programName, outFolder string, data *u.Data) {
|
|||
programName+"_shared_libs", data.DynamicData.SharedLibs, nil)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
// /!\ MISSING "/" !!!
|
||||
stringFile := "#include<stdlib.h>\n/* #include ta mère *\nint main() {\n\t// Salut bitch !\n\treturn 0;\n}"
|
||||
|
||||
for {
|
||||
comStartIndex := strings.Index(stringFile, "/*")
|
||||
if comStartIndex != -1 {
|
||||
comEndIndex := strings.Index(stringFile, "*")
|
||||
stringFile = strings.Join([]string{stringFile[:comStartIndex],
|
||||
stringFile[comEndIndex+2:]}, "")
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
//what to do with "\t" in lines ?
|
||||
var finalFile []string
|
||||
sliceFile := strings.Split(stringFile, "\n")
|
||||
for i := 0; i < len(sliceFile); i++ {
|
||||
if !strings.HasPrefix(sliceFile[i], "//") {
|
||||
finalFile = append(finalFile, sliceFile[i])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove dependencies whose files are not in program directory (e.g., stdio, stdlib, ...)
|
||||
for internalFile, dependencies := range interdependMap {
|
||||
var internalDep []string
|
||||
for _, dependency := range dependencies {
|
||||
if _, ok := interdependMap[dependency]; ok {
|
||||
a++
|
||||
internalDep = append(internalDep, dependency)
|
||||
}
|
||||
}
|
||||
interdependMap[internalFile] = internalDep
|
||||
}
|
||||
|
||||
// Detect and print removable program source files (i.e., files that no other file depends
|
||||
// on)
|
||||
var removableFiles []string
|
||||
for internalFile := range interdependMap {
|
||||
depends := false
|
||||
for _, dependencies := range interdependMap {
|
||||
for _, dependency := range dependencies {
|
||||
if internalFile == dependency {
|
||||
depends = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if depends {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !depends {
|
||||
removableFiles = append(removableFiles, internalFile)
|
||||
}
|
||||
}
|
||||
fmt.Println("Removable program source files of ", programName, ":")
|
||||
fmt.Println(removableFiles)
|
||||
*/
|
||||
|
|
|
@ -194,6 +194,25 @@ func executeDependAptCache(programName string, data *u.StaticData,
|
|||
return nil
|
||||
}
|
||||
|
||||
// getProgramFolder gets the folder path in which the given program is located, according to the
|
||||
// Unikraft standard (e.g., /home/.../apps/programFolder/.../program).
|
||||
//
|
||||
// It returns the folder containing the program files according to the standard described above.
|
||||
func getProgramFolder(programPath string) string {
|
||||
|
||||
tmp := strings.Split(programPath, "/")
|
||||
i := 2
|
||||
|
||||
for ; i < len(tmp); i++ {
|
||||
if tmp[len(tmp)-i] == "apps" {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
folderPath := strings.Join(tmp[:len(tmp)-i+2], "/")
|
||||
return folderPath
|
||||
}
|
||||
|
||||
// findSourcesFiles puts together all C/C++ source files found in a given application folder.
|
||||
//
|
||||
// It returns a slice containing the found source file names and an error if any, otherwise it
|
||||
|
@ -228,10 +247,7 @@ func findSourcesFiles(workspace string) ([]string, error) {
|
|||
// it returns nil.
|
||||
func ExecuteCommand(command string, arguments []string) (string, error) {
|
||||
out, err := exec.Command(command, arguments...).CombinedOutput()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(out), nil
|
||||
return string(out), err
|
||||
}
|
||||
|
||||
// addSourceFileSymbols adds all the symbols present in 'output' to the static data field in
|
||||
|
@ -275,21 +291,12 @@ func extractPrototype(sourcesFiltered []string, data *u.Data) error {
|
|||
// It returns an error if any, otherwise it returns nil.
|
||||
func gatherSourceFileSymbols(data *u.Data, programPath string) error {
|
||||
|
||||
tmp := strings.Split(programPath, "/")
|
||||
i := 2
|
||||
for ; i < len(tmp); i++ {
|
||||
if tmp[len(tmp)-i] == "apps" {
|
||||
break
|
||||
}
|
||||
}
|
||||
folderPath := strings.Join(tmp[:len(tmp)-i+2], "/")
|
||||
|
||||
files, err := findSourcesFiles(folderPath)
|
||||
sourceFiles, err := findSourcesFiles(getProgramFolder(programPath))
|
||||
if err != nil {
|
||||
u.PrintErr(err)
|
||||
}
|
||||
|
||||
if err := extractPrototype(files, data); err != nil {
|
||||
if err := extractPrototype(sourceFiles, data); err != nil {
|
||||
u.PrintErr(err)
|
||||
}
|
||||
return nil
|
||||
|
|
Loading…
Add table
Reference in a new issue