Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass options via a json file instead of command line arguments #167

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
fe7a8ed
Parse the arguments at the shim and create a temp json file
ZeyadTarekk Jul 18, 2024
ba65ddc
Keep the created file
ZeyadTarekk Jul 18, 2024
46df932
Parse the arguments from the JSON file at cpp
ZeyadTarekk Jul 18, 2024
9c47a3f
Remove a new line in the beginning of function
ZeyadTarekk Jul 19, 2024
9eca26d
Set environment variables in a separate function
ZeyadTarekk Jul 19, 2024
ca221e9
Remove the old options function
ZeyadTarekk Jul 19, 2024
d91949f
Return the filename without intermediate variable
ZeyadTarekk Jul 19, 2024
c72ddf6
Return option json and write the file at the main
ZeyadTarekk Jul 19, 2024
9175f55
Set the environemnt variables outside of the options method
ZeyadTarekk Jul 22, 2024
0a74604
Remove debugging logs
ZeyadTarekk Jul 22, 2024
313e36a
Edit some arguments type
ZeyadTarekk Jul 22, 2024
ef2359b
Handle extra_analysis_arguments in a different way
ZeyadTarekk Jul 22, 2024
ea810d7
use boost program options to get the config file path
ZeyadTarekk Jul 22, 2024
6cff675
Remove parsing the json from the main
ZeyadTarekk Jul 23, 2024
f5168b6
Create static function to parse the arguments
ZeyadTarekk Jul 23, 2024
e514945
Remove couts
ZeyadTarekk Jul 23, 2024
f46ce38
parse the options from the json in the run method
ZeyadTarekk Jul 23, 2024
28d1e36
return unique_ptr instead of option ptr
ZeyadTarekk Jul 23, 2024
7275b9a
Edit return type implementation
ZeyadTarekk Jul 23, 2024
ef66403
Handle unique_ptr
ZeyadTarekk Jul 23, 2024
e8184ef
Remove the intermediate variable
ZeyadTarekk Jul 23, 2024
dd52686
Parse the json file in the new constructor
ZeyadTarekk Jul 23, 2024
9942cd9
Remove old unused methods
ZeyadTarekk Jul 23, 2024
c00ac54
Add more validation methods
ZeyadTarekk Jul 23, 2024
3401b48
Validate the json values using JsonValidation
ZeyadTarekk Jul 23, 2024
0d87ac1
pass the jar paths as array of strings
ZeyadTarekk Jul 23, 2024
8ab7b89
string_list json validation
ZeyadTarekk Jul 23, 2024
8fbcfbd
Parse jar paths as string_list
ZeyadTarekk Jul 23, 2024
5fb9c8a
remove string_list
ZeyadTarekk Jul 24, 2024
14f6b03
retrurn system jar files as string
ZeyadTarekk Jul 24, 2024
269d189
rename from_json_file method
ZeyadTarekk Jul 24, 2024
dfe8602
accept std::filesystem::path
ZeyadTarekk Jul 24, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
191 changes: 100 additions & 91 deletions shim/shim.py
Original file line number Diff line number Diff line change
Expand Up @@ -612,147 +612,152 @@ def _add_debug_arguments(parser: argparse.ArgumentParser) -> None:
)


def _get_command_options(
def _set_environment_variables(arguments):
trace_settings = [f"MARIANA_TRENCH:{arguments.verbosity}"]
if "TRACE" in os.environ:
trace_settings.insert(0, os.environ["TRACE"])
os.environ["TRACE"] = ",".join(trace_settings)

def _get_command_options_json(
arguments: argparse.Namespace, apk_directory: str, dex_directory: str
) -> List[str]:
options = [
"--system-jar-paths",
arguments.system_jar_configuration_path,
"--apk-directory",
apk_directory,
"--dex-directory",
dex_directory,
"--rules-paths",
arguments.rules_paths,
"--repository-root-directory",
arguments.repository_root_directory,
"--source-root-directory",
arguments.source_root_directory,
"--apk-path",
arguments.apk_path,
"--output-directory",
arguments.output_directory,
"--maximum-source-sink-distance",
str(arguments.maximum_source_sink_distance),
"--model-generator-configuration-paths",
arguments.model_generator_configuration_paths,
]
) -> dict:
options_json = {}
options_json["system-jar-paths"] = arguments.system_jar_configuration_path
options_json["apk-directory"] = apk_directory
options_json["dex-directory"] = dex_directory
options_json["rules-paths"] = arguments.rules_paths
options_json["repository-root-directory"] = arguments.repository_root_directory
options_json["source-root-directory"] = arguments.source_root_directory
options_json["apk-path"] = arguments.apk_path
options_json["output-directory"] = arguments.output_directory
options_json["maximum-source-sink-distance"] = arguments.maximum_source_sink_distance
options_json["model-generator-configuration-paths"] = arguments.model_generator_configuration_paths

if arguments.grepo_metadata_path:
options.append("--grepo-metadata-path")
options.append(arguments.grepo_metadata_path)
options_json["grepo-metadata-path"] = arguments.grepo_metadata_path

if arguments.model_generator_search_paths:
options.append("--model-generator-search-paths")
options.append(arguments.model_generator_search_paths)
options_json["model-generator-search-paths"] = arguments.model_generator_search_paths

if arguments.models_paths:
options.append("--models-paths")
options.append(arguments.models_paths)
options_json["models-paths"] = arguments.models_paths

if arguments.field_models_paths:
options.append("--field-models-paths")
options.append(arguments.field_models_paths)
options_json["field-models-paths"] = arguments.field_models_paths

if arguments.literal_models_paths:
options.append("--literal-models-paths")
options.append(arguments.literal_models_paths)
options_json["literal-models-paths"] = arguments.literal_models_paths

if arguments.proguard_configuration_paths:
options.append("--proguard-configuration-paths")
options.append(arguments.proguard_configuration_paths)
options_json["proguard-configuration-paths"] = arguments.proguard_configuration_paths

if arguments.lifecycles_paths:
options.append("--lifecycles-paths")
options.append(arguments.lifecycles_paths)
options_json["lifecycles-paths"] = arguments.lifecycles_paths

if arguments.shims_paths:
options.append("--shims-paths")
options.append(arguments.shims_paths)
options_json["shims-paths"] = arguments.shims_paths

if arguments.graphql_metadata_paths:
options.append("--graphql-metadata-paths")
options.append(arguments.graphql_metadata_paths)
options_json["graphql-metadata-paths"] = arguments.graphql_metadata_paths

if arguments.source_exclude_directories:
options.append("--source-exclude-directories")
options.append(arguments.source_exclude_directories)
options_json["source-exclude-directories"] = arguments.source_exclude_directories

if arguments.generated_models_directory:
options.append("--generated-models-directory")
options.append(arguments.generated_models_directory)
options_json["generated-models-directory"] = arguments.generated_models_directory

if arguments.sharded_models_directory:
options.append("--sharded-models-directory")
options.append(arguments.sharded_models_directory)
options_json["sharded-models-directory"] = arguments.sharded_models_directory

if arguments.emit_all_via_cast_features:
options.append("--emit-all-via-cast-features")
options_json["emit-all-via-cast-features"] = True

if arguments.propagate_across_arguments:
options.append("--propagate-across-arguments")
options_json["propagate-across-arguments"] = True

if arguments.allow_via_cast_feature:
options_json["allow-via-cast-feature"] = []
for feature in arguments.allow_via_cast_feature:
options.append("--allow-via-cast-feature=%s" % feature.strip())
options_json["allow-via-cast-feature"].append(feature.strip())

if arguments.heuristics:
options.append("--heuristics")
options.append(arguments.heuristics)
options_json["heuristics"] = arguments.heuristics

if arguments.sequential:
options.append("--sequential")
options_json["sequential"] = True

if arguments.skip_source_indexing:
options.append("--skip-source-indexing")
options_json["skip-source-indexing"] = True

if arguments.skip_analysis:
options.append("--skip-analysis")
options_json["skip-analysis"] = True

if arguments.disable_parameter_type_overrides:
options.append("--disable-parameter-type-overrides")
options_json["disable-parameter-type-overrides"] = True

if arguments.disable_global_type_analysis:
options.append("--disable-global-type-analysis")
options_json["disable-global-type-analysis"] = True

if arguments.verify_expected_output:
options.append("--verify-expected-output")
options_json["verify-expected-output"] = True

if arguments.remove_unreachable_code:
options.append("--remove-unreachable-code")
options_json["remove-unreachable-code"] = True

if arguments.maximum_method_analysis_time is not None:
options.append("--maximum-method-analysis-time")
options.append(str(arguments.maximum_method_analysis_time))
options_json["maximum-method-analysis-time"] = arguments.maximum_method_analysis_time

if arguments.enable_cross_component_analysis:
options.append("--enable-cross-component-analysis")
options_json["enable-cross-component-analysis"] = True

if arguments.extra_analysis_arguments:
options.extend(shlex.split(arguments.extra_analysis_arguments))
extra_arguments = json.loads(arguments.extra_analysis_arguments)
for key, value in extra_arguments.items():
if key in options_json and isinstance(options_json[key],list) and isinstance(value, list):
# Append the values to the existing list
options_json[key].extend(value)
else:
# Overwrite the value
options_json[key] = value

if arguments.job_id:
options.append("--job-id")
options.append(arguments.job_id)
if arguments.metarun_id:
options.append("--metarun-id")
options.append(arguments.metarun_id)
options_json["job-id"] = arguments.job_id

trace_settings = [f"MARIANA_TRENCH:{arguments.verbosity}"]
if "TRACE" in os.environ:
trace_settings.insert(0, os.environ["TRACE"])
os.environ["TRACE"] = ",".join(trace_settings)
if arguments.metarun_id:
options_json["metarun-id"] = arguments.metarun_id

if arguments.log_method:
options_json["log-method"] = []
for method in arguments.log_method:
options.append("--log-method=%s" % method.strip())
options_json["log-method"].append(method.strip())

if arguments.log_method_types:
options_json["log-method-types"] = []
for method in arguments.log_method_types:
options.append("--log-method-types=%s" % method.strip())
options_json["log-method-types"].append(method.strip())

if arguments.dump_class_hierarchies:
options.append("--dump-class-hierarchies")
options_json["dump-class-hierarchies"] = True

if arguments.dump_overrides:
options.append("--dump-overrides")
options_json["dump-overrides"] = True

if arguments.dump_call_graph:
options.append("--dump-call-graph")
options_json["dump-call-graph"] = True

if arguments.dump_dependencies:
options.append("--dump-dependencies")
options_json["dump-dependencies"] = True

if arguments.dump_methods:
options.append("--dump-methods")
options_json["dump-methods"] = True

if arguments.dump_coverage_info:
options.append("--dump-coverage-info")
options_json["dump-coverage-info"] = True

if arguments.always_export_origins:
options.append("--always-export-origins")
return options
options_json["always-export-origins"] = True

return options_json


def main() -> None:
Expand Down Expand Up @@ -855,14 +860,18 @@ def main() -> None:
binary, arguments, apk_directory, dex_directory
ZeyadTarekk marked this conversation as resolved.
Show resolved Hide resolved
)
else:
options = _get_command_options(arguments, apk_directory, dex_directory)
command = [os.fspath(binary.resolve())] + options
if arguments.gdb:
command = ["gdb", "--args"] + command
elif arguments.lldb:
command = ["lldb", "--"] + command
LOG.info(f"Running Mariana Trench: {' '.join(command)}")
output = subprocess.run(command)
with tempfile.NamedTemporaryFile(suffix=".json",mode="w") as options_file:
_set_environment_variables(arguments)
options_json = _get_command_options_json(arguments, apk_directory, dex_directory)
json.dump(options_json, options_file, indent=4)
options_file.flush()
command = [os.fspath(binary.resolve()),"--config" ,options_file.name]
if arguments.gdb:
command = ["gdb", "--args"] + command
elif arguments.lldb:
command = ["lldb", "--"] + command
LOG.info(f"Running Mariana Trench: {' '.join(command)}")
output = subprocess.run(command)
if output.returncode != 0:
LOG.fatal(f"Analysis binary exited with exit code {output.returncode}.")
sys.exit(output.returncode)
Expand Down
32 changes: 32 additions & 0 deletions source/JsonValidation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,22 @@ std::string JsonValidation::string(
return string.asString();
}

std::optional<std::string> JsonValidation::optional_string(
const Json::Value& value,
const std::string& field) {
validate_object(
value, fmt::format("non-null object with string field `{}`", field));
const auto& string = value[field];
if (string.isNull()) {
return std::nullopt;
}
if (!string.isString()) {
throw JsonValidationError(value, field, /* expected */ "string");
}
return string.asString();
}


int JsonValidation::integer(const Json::Value& value) {
if (value.isNull() || !value.isInt()) {
throw JsonValidationError(
Expand Down Expand Up @@ -162,6 +178,22 @@ bool JsonValidation::boolean(
return boolean.asBool();
}

bool JsonValidation::optional_boolean(
const Json::Value& value,
const std::string& field,
bool default_value) {
validate_object(
value, fmt::format("non-null object with boolean field `{}`", field));
const auto& boolean = value[field];
if (boolean.isNull()) {
return default_value;
}
if (!boolean.isBool()) {
throw JsonValidationError(value, field, /* expected */ "boolean");
}
return boolean.asBool();
}

const Json::Value& JsonValidation::null_or_array(const Json::Value& value) {
if (!value.isNull() && !value.isArray()) {
throw JsonValidationError(
Expand Down
2 changes: 2 additions & 0 deletions source/JsonValidation.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ class JsonValidation final {

static std::string string(const Json::Value& value);
static std::string string(const Json::Value& value, const std::string& field);
static std::optional<std::string> optional_string(const Json::Value& value, const std::string& field);

static int integer(const Json::Value& value);
static int integer(const Json::Value& value, const std::string& field);
Expand All @@ -54,6 +55,7 @@ class JsonValidation final {

static bool boolean(const Json::Value& value);
static bool boolean(const Json::Value& value, const std::string& field);
static bool optional_boolean(const Json::Value& value, const std::string& field,bool default_value);

static const Json::Value& null_or_array(const Json::Value& value);
static const Json::Value& null_or_array(
Expand Down
19 changes: 12 additions & 7 deletions source/Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
#include <RedexContext.h>

#include <mariana-trench/ExitCode.h>
#include <mariana-trench/JsonReaderWriter.h>
#include <mariana-trench/JsonValidation.h>
#include <mariana-trench/GlobalRedexContext.h>
#include <mariana-trench/LifecycleMethods.h>
#include <mariana-trench/MarianaTrench.h>
Expand All @@ -34,23 +36,26 @@ int main(int argc, char* argv[]) {

namespace program_options = boost::program_options;
program_options::options_description options;
options.add_options()("help,h", "Show help dialog.");
options.add_options()
("help,h", "Show help dialog.")
("config,c", program_options::value<std::string>()->required(), "Path to the JSON configuration file.");

auto tool = marianatrench::MarianaTrench();
tool.add_options(options);

try {
program_options::variables_map variables;
program_options::store(
program_options::command_line_parser(argc, argv).options(options).run(),
variables);

program_options::store(program_options::parse_command_line(argc, argv, options), variables);
if (variables.count("help")) {
std::cerr << options;
return 0;
}

program_options::notify(variables);
if (!variables.count("config")) {
std::cerr << "No JSON configuration file provided.\n";
std::cerr << "Usage: " << argv[0] << " --config <json_config_file>\n";
return ExitCode::invalid_argument_error("No JSON configuration file provided.");
}

ZeyadTarekk marked this conversation as resolved.
Show resolved Hide resolved

marianatrench::GlobalRedexContext redex_context(
/* allow_class_duplicates */ true);
Expand Down
7 changes: 2 additions & 5 deletions source/MarianaTrench.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,6 @@ MarianaTrench::MarianaTrench()

namespace program_options = boost::program_options;

void MarianaTrench::add_options(
program_options::options_description& options) const {
Options::add_options(options);
}

Registry MarianaTrench::analyze(Context& context) {
context.artificial_methods = std::make_unique<ArtificialMethods>(
Expand Down Expand Up @@ -423,8 +419,9 @@ std::vector<std::string> filter_existing_jars(

void MarianaTrench::run(const program_options::variables_map& variables) {
Context context;
std::filesystem::path json_file_path = std::filesystem::path(variables["config"].as<std::string>());

context.options = std::make_unique<Options>(variables);
context.options = Options::from_json_file(json_file_path);
const auto& options = *context.options;

if (auto heuristics_path = options.heuristics_path()) {
Expand Down
2 changes: 0 additions & 2 deletions source/MarianaTrench.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ class MarianaTrench : public Tool {
public:
MarianaTrench();

void add_options(
boost::program_options::options_description& options) const override;
void run(const boost::program_options::variables_map& variables) override;

private:
Expand Down
Loading
Loading