Commit e8d4f5c2 authored by Millian Poquet's avatar Millian Poquet
Browse files

Towards dynamic jobs: memory management.

Dynamic jobs are now added into Batsim data structures when the server
received a JOB_SUBMITTED message if needed.

To do so, methods have been added in the data structures (Jobs, Profiles,
Workloads...).

Furthermore, the JSON parsing is a bit cleaner now to avoid code redundancy.
parent b0a3b6a9
......@@ -164,6 +164,13 @@ IPMessage::~IPMessage()
data = nullptr;
}
JobIdentifier::JobIdentifier(const string &workload_name, int job_number) :
workload_name(workload_name),
job_number(job_number)
{
}
string JobIdentifier::to_string() const
{
return workload_name + '!' + std::to_string(job_number);
......
......@@ -19,6 +19,13 @@ struct BatsimContext;
*/
struct JobIdentifier
{
/**
* @brief Creates a JobIdentifier
* @param[in] workload_name The workload name
* @param[in] job_number The job number
*/
JobIdentifier(const std::string & workload_name = "", int job_number = -1);
std::string workload_name; //!< The name of the workload the job belongs to
int job_number; //!< The job unique number inside its workload
......
......@@ -78,9 +78,9 @@ int static_job_submitter_process(int argc, char *argv[])
MSG_process_sleep(job->submission_time - previousSubmissionDate);
// Let's put the metadata about the job into the data storage
string job_id_string = args->workload_name + "!" + to_string(job->number);
string job_key = "job_" + job_id_string;
string profile_key = "profile_" + job_id_string;
JobIdentifier job_id(workload->name, job->number);
string job_key = RedisStorage::job_key(job_id);
string profile_key = RedisStorage::profile_key(workload->name, job->profile);
context->storage.set(job_key, job->json_description);
context->storage.set(profile_key, workload->profiles->at(job->profile)->json_description);
......
......@@ -59,74 +59,51 @@ void Jobs::load_from_json(const Document &doc, const string &filename)
for (SizeType i = 0; i < jobs.Size(); i++) // Uses SizeType instead of size_t
{
Job * j = new Job;
j->workload = _workload;
j->starting_time = -1;
j->runtime = -1;
j->state = JobState::JOB_STATE_NOT_SUBMITTED;
j->consumed_energy = -1;
const Value & job = jobs[i];
xbt_assert(job.IsObject(), "Invalid JSON file '%s': one job is not an object", filename.c_str());
xbt_assert(job.HasMember("id"), "Invalid JSON file '%s': one job has no 'id' field", filename.c_str());
xbt_assert(job["id"].IsInt(), "Invalid JSON file '%s': one job has a non-integral 'id' field ('%s')", filename.c_str(), job["id"].GetString());
j->number = job["id"].GetInt();
xbt_assert(job.HasMember("subtime"), "Invalid JSON file '%s': job %d has no 'subtime' field", filename.c_str(), j->number);
xbt_assert(job["subtime"].IsNumber(), "Invalid JSON file '%s': job %d has a non-number 'subtime' field", filename.c_str(), j->number);
j->submission_time = job["subtime"].GetDouble();
xbt_assert(job.HasMember("walltime"), "Invalid JSON file '%s': job %d has no 'walltime' field", filename.c_str(), j->number);
xbt_assert(job["walltime"].IsNumber(), "Invalid JSON file '%s': job %d has a non-number 'walltime' field", filename.c_str(), j->number);
j->walltime = job["walltime"].GetDouble();
xbt_assert(job.HasMember("res"), "Invalid JSON file '%s': job %d has no 'res' field", filename.c_str(), j->number);
xbt_assert(job["res"].IsInt(), "Invalid JSON file '%s': job %d has a non-number 'res' field", filename.c_str(), j->number);
j->required_nb_res = job["res"].GetInt();
xbt_assert(job.HasMember("profile"), "Invalid JSON file '%s': job %d has no 'profile' field", filename.c_str(), j->number);
xbt_assert(job["profile"].IsString(), "Invalid JSON file '%s': job %d has a non-string 'profile' field", filename.c_str(), j->number);
j->profile = job["profile"].GetString();
// Let's get the JSON string which describes the job
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
job.Accept(writer);
j->json_description = buffer.GetString();
const Value & job_json_description = jobs[i];
Job * j = Job::from_json(job_json_description, _workload);
xbt_assert(!exists(j->number), "Invalid JSON file '%s': duplication of job id %d", filename.c_str(), j->number);
_jobs[j->number] = j;
}
}
Job *Jobs::operator[](int job_id)
Job *Jobs::operator[](int job_number)
{
auto it = _jobs.find(job_id);
xbt_assert(it != _jobs.end(), "Cannot get job %d: it does not exist", job_id);
auto it = _jobs.find(job_number);
xbt_assert(it != _jobs.end(), "Cannot get job %d: it does not exist", job_number);
return it->second;
}
const Job *Jobs::operator[](int job_id) const
const Job *Jobs::operator[](int job_number) const
{
auto it = _jobs.find(job_id);
xbt_assert(it != _jobs.end(), "Cannot get job %d: it does not exist", job_id);
auto it = _jobs.find(job_number);
xbt_assert(it != _jobs.end(), "Cannot get job %d: it does not exist", job_number);
return it->second;
}
Job *Jobs::at(int job_id)
Job *Jobs::at(int job_number)
{
return operator[](job_number);
}
const Job *Jobs::at(int job_number) const
{
return operator[](job_id);
return operator[](job_number);
}
const Job *Jobs::at(int job_id) const
void Jobs::add_job(Job *job)
{
return operator[](job_id);
xbt_assert(!exists(job->number),
"Bad Jobs::add_job call: A job with number=%d already exists.",
job->number);
_jobs[job->number] = job;
}
bool Jobs::exists(int job_id) const
bool Jobs::exists(int job_number) const
{
auto it = _jobs.find(job_id);
auto it = _jobs.find(job_number);
return it != _jobs.end();
}
......@@ -170,3 +147,51 @@ bool job_comparator_subtime(const Job *a, const Job *b)
{
return a->submission_time < b->submission_time;
}
Job * Job::from_json(const rapidjson::Value & json_desc, Workload * workload)
{
Job * j = new Job;
j->workload = workload;
j->starting_time = -1;
j->runtime = -1;
j->state = JobState::JOB_STATE_NOT_SUBMITTED;
j->consumed_energy = -1;
xbt_assert(json_desc.IsObject(), "Invalid JSON: one job is not an object");
xbt_assert(json_desc.HasMember("id"), "Invalid JSON: one job has no 'id' field");
xbt_assert(json_desc["id"].IsInt(), "Invalid JSON: one job has a non-integral 'id' field ('%s')", json_desc["id"].GetString());
j->number = json_desc["id"].GetInt();
xbt_assert(json_desc.HasMember("subtime"), "Invalid JSON: job %d has no 'subtime' field", j->number);
xbt_assert(json_desc["subtime"].IsNumber(), "Invalid JSON: job %d has a non-number 'subtime' field", j->number);
j->submission_time = json_desc["subtime"].GetDouble();
xbt_assert(json_desc.HasMember("walltime"), "Invalid JSON: job %d has no 'walltime' field", j->number);
xbt_assert(json_desc["walltime"].IsNumber(), "Invalid JSON: job %d has a non-number 'walltime' field", j->number);
j->walltime = json_desc["walltime"].GetDouble();
xbt_assert(json_desc.HasMember("res"), "Invalid JSON: job %d has no 'res' field", j->number);
xbt_assert(json_desc["res"].IsInt(), "Invalid JSON: job %d has a non-number 'res' field", j->number);
j->required_nb_res = json_desc["res"].GetInt();
xbt_assert(json_desc.HasMember("profile"), "Invalid JSON: job %d has no 'profile' field", j->number);
xbt_assert(json_desc["profile"].IsString(), "Invalid JSON: job %d has a non-string 'profile' field", j->number);
j->profile = json_desc["profile"].GetString();
// Let's get the JSON string which describes the job (to conserve potential fields unused by Batsim)
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
json_desc.Accept(writer);
j->json_description = buffer.GetString();
return j;
}
Job * Job::from_json(const std::string & json_str, Workload *workload)
{
Document doc;
doc.Parse(json_str.c_str());
return Job::from_json(doc, workload);
}
......@@ -48,6 +48,26 @@ struct Job
double runtime; //!< The amount of time during which the job has been executed
MachineRange allocation; //!< The machines on which the job has been executed.
JobState state; //!< The current state of the job
/**
* @brief Creates a new-allocated Job from a JSON description
* @param[in] json_desc The JSON description of the job
* @param[in] workload The Workload the job is in
* @return The newly allocated Job
* @pre The JSON description of the job is valid
*/
static Job * from_json(const rapidjson::Value & json_desc,
Workload * workload);
/**
* @brief Creates a new-allocated Job from a JSON description
* @param[in] json_str The JSON description of the job (as a string)
* @param[in] workload The Workload the job is in
* @return The newly allocated Job
* @pre The JSON description of the job is valid
*/
static Job * from_json(const std::string & json_str,
Workload * workload);
};
/**
......@@ -70,6 +90,7 @@ public:
Jobs();
/**
* @brief Destroys a Jobs
* @details All Job instances will be deleted
*/
~Jobs();
......@@ -94,38 +115,45 @@ public:
/**
* @brief Accesses one job thanks to its unique number
* @param[in] job_id The job unique number
* @param[in] job_number The job unique number
* @return A pointer to the job associated to the given job number
*/
Job * operator[](int job_id);
Job * operator[](int job_number);
/**
* @brief Accesses one job thanks to its unique number (const version)
* @param[in] job_id The job unique number
* @param[in] job_number The job unique number
* @return A (const) pointer to the job associated to the given job number
*/
const Job * operator[](int job_id) const;
const Job * operator[](int job_number) const;
/**
* @brief Accesses one job thanks to its unique number
* @param[in] job_id The job unique number
* @param[in] job_number The job unique number
* @return A pointer to the job associated to the given job number
*/
Job * at(int job_id);
Job * at(int job_number);
/**
* @brief Accesses one job thanks to its unique number (const version)
* @param[in] job_id The job unique number
* @param[in] job_number The job unique number
* @return A (const) pointer to the job associated to the given job number
*/
const Job * at(int job_id) const;
const Job * at(int job_number) const;
/**
* @brief Adds a job into a Jobs instance
* @param[in] job The job to add
* @pre No job with the same number exist in the Jobs instance
*/
void add_job(Job * job);
/**
* @brief Allows to know whether a job exists
* @param[in] job_id The unique job number
* @param[in] job_number The unique job number
* @return True if and only if a job with the given job number exists
*/
bool exists(int job_id) const;
bool exists(int job_number) const;
/**
* @brief Allows to know whether the Jobs contains any SMPI job
......
......@@ -47,135 +47,16 @@ void Profiles::load_from_json(const Document &doc, const string & filename)
const Value & key = it->name;
const Value & value = it->value;
xbt_assert(key.IsString(), "Invalid JSON file '%s': all children of the 'profiles' object must have a string key", filename.c_str());
xbt_assert(value.IsObject(), "Invalid JSON file '%s': profile '%s' value must be an object", filename.c_str(), key.GetString());
xbt_assert(value.HasMember("type"), "Invalid JSON file '%s': profile '%s' has no 'type' field", filename.c_str(), key.GetString());
xbt_assert(value["type"].IsString(), "Invalid JSON file '%s': profile '%s' has a non-string 'type' field", filename.c_str(), key.GetString());
xbt_assert(key.IsString(),
"Invalid JSON file '%s': all children of the 'profiles' object must have a string key",
filename.c_str());
string profile_name = key.GetString();
Profile * profile = new Profile;
Profile * profile = Profile::from_json(profile_name, value);
string profileType = value["type"].GetString();
if (profileType == "delay")
{
profile->type = ProfileType::DELAY;
DelayProfileData * data = new DelayProfileData;
xbt_assert(value.HasMember("delay"), "Invalid JSON file '%s': profile '%s' has no 'delay' field", filename.c_str(), key.GetString());
xbt_assert(value["delay"].IsNumber(), "Invalid JSON file '%s': profile '%s' has a non-number 'delay' field", filename.c_str(), key.GetString());
data->delay = value["delay"].GetDouble();
profile->data = data;
}
else if (profileType == "msg_par")
{
profile->type = ProfileType::MSG_PARALLEL;
MsgParallelProfileData * data = new MsgParallelProfileData;
xbt_assert(value.HasMember("cpu"), "Invalid JSON file '%s': profile '%s' has no 'cpu' field", filename.c_str(), key.GetString());
const Value & cpu = value["cpu"];
xbt_assert(cpu.IsArray(), "Invalid JSON file '%s': profile '%s' has a non-array 'cpu' field", filename.c_str(), key.GetString());
data->nb_res = cpu.Size();
xbt_assert(data->nb_res > 0, "Invalid JSON file '%s': profile '%s' has an invalid-sized array 'cpu' (size=%d): must be strictly positive", filename.c_str(), key.GetString(), (int)cpu.Size());
xbt_assert((int)cpu.Size() == data->nb_res, "Invalid JSON file '%s' : profile '%s' is incoherent: cpu array has size %d whereas nb_res is %d", filename.c_str(), key.GetString(), cpu.Size(), data->nb_res);
data->cpu = new double[data->nb_res];
for (unsigned int i = 0; i < cpu.Size(); ++i)
{
xbt_assert(cpu[i].IsNumber(), "Invalid JSON file '%s': profile '%s' computation array is invalid: all elements must be numbers", filename.c_str(), key.GetString());
data->cpu[i] = cpu[i].GetDouble();
}
xbt_assert(value.HasMember("com"), "Invalid JSON file '%s': profile '%s' has no 'com' field", filename.c_str(), key.GetString());
const Value & com = value["com"];
xbt_assert(com.IsArray(), "Invalid JSON file '%s': profile '%s' has a non-array 'com' field", filename.c_str(), key.GetString());
xbt_assert((int)com.Size() == data->nb_res * data->nb_res, "Invalid JSON file '%s' : profile '%s' is incoherent: com array has size %d whereas nb_res is %d", filename.c_str(), key.GetString(), com.Size(), data->nb_res);
data->com = new double[data->nb_res * data->nb_res];
for (unsigned int i = 0; i < com.Size(); ++i)
{
xbt_assert(com[i].IsNumber(), "Invalid JSON file '%s': profile '%s' communication array is invalid: all elements must be numbers", filename.c_str(), key.GetString());
data->com[i] = com[i].GetDouble();
xbt_assert(data->com[i] >= 0, "Invalid JSON file '%s': profile '%s' communication array is invalid: all elements must be non-negative", filename.c_str(), key.GetString());
}
profile->data = data;
}
else if (profileType == "msg_par_hg")
{
profile->type = ProfileType::MSG_PARALLEL_HOMOGENEOUS;
MsgParallelHomogeneousProfileData * data = new MsgParallelHomogeneousProfileData;
xbt_assert(value.HasMember("cpu"), "Invalid JSON file '%s': profile '%s' has no 'cpu' field", filename.c_str(), key.GetString());
xbt_assert(value["cpu"].IsNumber(), "Invalid JSON file '%s': profile '%s' has a non-number 'cpu' field", filename.c_str(), key.GetString());
data->cpu = value["cpu"].GetDouble();
xbt_assert(value.HasMember("com"), "Invalid JSON file '%s': profile '%s' has no 'com' field", filename.c_str(), key.GetString());
xbt_assert(value["com"].IsNumber(), "Invalid JSON file '%s': profile '%s' has a non-number 'com' field", filename.c_str(), key.GetString());
data->com = value["com"].GetDouble();
profile->data = data;
}
else if (profileType == "composed")
{
profile->type = ProfileType::SEQUENCE;
SequenceProfileData * data = new SequenceProfileData;
xbt_assert(value.HasMember("nb"), "Invalid JSON file '%s': profile '%s' has no 'nb' field", filename.c_str(), key.GetString());
xbt_assert(value["nb"].IsInt(), "Invalid JSON file '%s': profile '%s' has a non-integral 'nb' field", filename.c_str(), key.GetString());
data->repeat = value["nb"].GetInt();
xbt_assert(data->repeat > 0, "Invalid JSON file '%s': profile '%s' has a non-strictly-positive 'nb' field (%d)", filename.c_str(), key.GetString(), data->repeat);
xbt_assert(value.HasMember("seq"), "Invalid JSON file '%s': profile '%s' has no 'seq' field", filename.c_str(), key.GetString());
xbt_assert(value["seq"].IsArray(), "Invalid JSON file '%s': profile '%s' has a non-array 'seq' field", filename.c_str(), key.GetString());
const Value & seq = value["seq"];
xbt_assert(seq.Size() > 0, "Invalid JSON file '%s': profile '%s' has an invalid array 'seq': its size must be strictly positive", filename.c_str(), key.GetString());
for (unsigned int i = 0; i < seq.Size(); ++i)
profile->data = data;
}
else if (profileType == "smpi")
{
profile->type = ProfileType::SMPI;
SmpiProfileData * data = new SmpiProfileData;
xbt_assert(value.HasMember("trace"), "Invalid JSON file '%s': profile '%s' has no 'trace' field", filename.c_str(), key.GetString());
xbt_assert(value["trace"].IsString(), "Invalid JSON file '%s': profile '%s' has a non-string 'trace' field", filename.c_str(), key.GetString());
const string trace = value["trace"].GetString();
filesystem::path baseDir(filename);
baseDir = baseDir.parent_path();
xbt_assert(filesystem::exists(baseDir) && filesystem::is_directory(baseDir));
//XBT_DEBUG("baseDir = '%s'", baseDir.string().c_str());
//XBT_DEBUG("trace = '%s'", trace.c_str());
filesystem::path tracePath(baseDir.string() + "/" + trace);
xbt_assert(filesystem::exists(tracePath) && filesystem::is_regular_file(tracePath),
"Invalid JSON file '%s': profile '%s' has an invalid 'trace' field ('%s'), which lead to a non-existent file ('%s')",
filename.c_str(), key.GetString(), trace.c_str(), string(filename + "/" + trace).c_str());
ifstream traceFile(tracePath.string());
xbt_assert(traceFile.is_open(), "Cannot open file '%s'", tracePath.string().c_str());
string line;
while (std::getline(traceFile, line))
{
trim_right(line);
filesystem::path rank_trace_path(tracePath.parent_path().string() + "/" + line);
data->trace_filenames.push_back("./" + rank_trace_path.string());
}
string filenames = boost::algorithm::join(data->trace_filenames, ", ");
XBT_DEBUG("Filenames of profile '%s': [%s]", key.GetString(), filenames.c_str());
profile->data = data;
}
// Let's get the JSON string which describes the profile
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
value.Accept(writer);
profile->json_description = buffer.GetString();
xbt_assert(!exists(string(key.GetString())), "Invalid JSON file '%s': duplication of profile name '%s'", filename.c_str(), key.GetString());
xbt_assert(!exists(string(key.GetString())),
"Invalid JSON file '%s': duplication of profile name '%s'",
filename.c_str(), key.GetString());
_profiles[string(key.GetString())] = profile;
}
}
......@@ -210,6 +91,16 @@ bool Profiles::exists(const std::string &profile_name) const
return mit != _profiles.end();
}
void Profiles::add_profile(const std::string & profile_name,
Profile *profile)
{
xbt_assert(!exists(profile_name),
"Bad Profiles::add_profile call: A profile with name='%s' already exists.",
profile_name.c_str());
_profiles[profile_name] = profile;
}
const std::map<std::string, Profile *> Profiles::profiles() const
{
return _profiles;
......@@ -280,3 +171,146 @@ Profile::~Profile()
else
XBT_ERROR("Deletion of an unknown profile type (%d)", type);
}
Profile *Profile::from_json(const std::string &profile_name, const rapidjson::Value &json_desc)
{
Profile * profile = new Profile;
xbt_assert(json_desc.IsObject(), "Invalid JSON: profile '%s' value must be an object", profile_name.c_str());
xbt_assert(json_desc.HasMember("type"), "Invalid JSON: profile '%s' has no 'type' field", profile_name.c_str());
xbt_assert(json_desc["type"].IsString(), "Invalid JSON: profile '%s' has a non-string 'type' field", profile_name.c_str());
string profile_type = json_desc["type"].GetString();
if (profile_type == "delay")
{
profile->type = ProfileType::DELAY;
DelayProfileData * data = new DelayProfileData;
xbt_assert(json_desc.HasMember("delay"), "Invalid JSON: profile '%s' has no 'delay' field", profile_name.c_str());
xbt_assert(json_desc["delay"].IsNumber(), "Invalid JSON: profile '%s' has a non-number 'delay' field", profile_name.c_str());
data->delay = json_desc["delay"].GetDouble();
profile->data = data;
}
else if (profile_type == "msg_par")
{
profile->type = ProfileType::MSG_PARALLEL;
MsgParallelProfileData * data = new MsgParallelProfileData;
xbt_assert(json_desc.HasMember("cpu"), "Invalid JSON: profile '%s' has no 'cpu' field", profile_name.c_str());
const Value & cpu = json_desc["cpu"];
xbt_assert(cpu.IsArray(), "Invalid JSON: profile '%s' has a non-array 'cpu' field", profile_name.c_str());
data->nb_res = cpu.Size();
xbt_assert(data->nb_res > 0, "Invalid JSON: profile '%s' has an invalid-sized array 'cpu' (size=%d): must be strictly positive", profile_name.c_str(), (int)cpu.Size());
xbt_assert((int)cpu.Size() == data->nb_res, "Invalid JSON : profile '%s' is incoherent: cpu array has size %d whereas nb_res is %d", profile_name.c_str(), cpu.Size(), data->nb_res);
data->cpu = new double[data->nb_res];
for (unsigned int i = 0; i < cpu.Size(); ++i)
{
xbt_assert(cpu[i].IsNumber(), "Invalid JSON: profile '%s' computation array is invalid: all elements must be numbers", profile_name.c_str());
data->cpu[i] = cpu[i].GetDouble();
}
xbt_assert(json_desc.HasMember("com"), "Invalid JSON: profile '%s' has no 'com' field", profile_name.c_str());
const Value & com = json_desc["com"];
xbt_assert(com.IsArray(), "Invalid JSON: profile '%s' has a non-array 'com' field", profile_name.c_str());
xbt_assert((int)com.Size() == data->nb_res * data->nb_res, "Invalid JSON : profile '%s' is incoherent: com array has size %d whereas nb_res is %d", profile_name.c_str(), com.Size(), data->nb_res);
data->com = new double[data->nb_res * data->nb_res];
for (unsigned int i = 0; i < com.Size(); ++i)
{
xbt_assert(com[i].IsNumber(), "Invalid JSON: profile '%s' communication array is invalid: all elements must be numbers", profile_name.c_str());
data->com[i] = com[i].GetDouble();
xbt_assert(data->com[i] >= 0, "Invalid JSON: profile '%s' communication array is invalid: all elements must be non-negative", profile_name.c_str());
}
profile->data = data;
}
else if (profile_type == "msg_par_hg")
{
profile->type = ProfileType::MSG_PARALLEL_HOMOGENEOUS;
MsgParallelHomogeneousProfileData * data = new MsgParallelHomogeneousProfileData;
xbt_assert(json_desc.HasMember("cpu"), "Invalid JSON: profile '%s' has no 'cpu' field", profile_name.c_str());
xbt_assert(json_desc["cpu"].IsNumber(), "Invalid JSON: profile '%s' has a non-number 'cpu' field", profile_name.c_str());
data->cpu = json_desc["cpu"].GetDouble();
xbt_assert(json_desc.HasMember("com"), "Invalid JSON: profile '%s' has no 'com' field", profile_name.c_str());
xbt_assert(json_desc["com"].IsNumber(), "Invalid JSON: profile '%s' has a non-number 'com' field", profile_name.c_str());
data->com = json_desc["com"].GetDouble();
profile->data = data;
}
else if (profile_type == "composed")
{
profile->type = ProfileType::SEQUENCE;
SequenceProfileData * data = new SequenceProfileData;
xbt_assert(json_desc.HasMember("nb"), "Invalid JSON: profile '%s' has no 'nb' field", profile_name.c_str());
xbt_assert(json_desc["nb"].IsInt(), "Invalid JSON: profile '%s' has a non-integral 'nb' field", profile_name.c_str());
data->repeat = json_desc["nb"].GetInt();
xbt_assert(data->repeat > 0, "Invalid JSON: profile '%s' has a non-strictly-positive 'nb' field (%d)", profile_name.c_str(), data->repeat);
xbt_assert(json_desc.HasMember("seq"), "Invalid JSON: profile '%s' has no 'seq' field", profile_name.c_str());
xbt_assert(json_desc["seq"].IsArray(), "Invalid JSON: profile '%s' has a non-array 'seq' field", profile_name.c_str());
const Value & seq = json_desc["seq"];
xbt_assert(seq.Size() > 0, "Invalid JSON: profile '%s' has an invalid array 'seq': its size must be strictly positive", profile_name.c_str());
for (unsigned int i = 0; i < seq.Size(); ++i)
profile->data = data;
}
else if (profile_type == "smpi")
{
xbt_assert(false, "Oops, SMPI jobs have been disabled (when dynamic jobs have been added). "
"Please update source code to make them work with static & dynamic jobs");
/*profile->type = ProfileType::SMPI;
SmpiProfileData * data = new SmpiProfileData;
xbt_assert(json_desc.HasMember("trace"), "Invalid JSON: profile '%s' has no 'trace' field", profile_name.c_str());
xbt_assert(json_desc["trace"].IsString(), "Invalid JSON: profile '%s' has a non-string 'trace' field", profile_name.c_str());
const string trace = json_desc["trace"].GetString();*/
/*filesystem::path baseDir(filename);
baseDir = baseDir.parent_path();
xbt_assert(filesystem::exists(baseDir) && filesystem::is_directory(baseDir));*/
//XBT_DEBUG("baseDir = '%s'", baseDir.string().c_str());
//XBT_DEBUG("trace = '%s'", trace.c_str());
/*filesystem::path tracePath(baseDir.string() + "/" + trace);
xbt_assert(filesystem::exists(tracePath) && filesystem::is_regular_file(tracePath),
"Invalid JSON: profile '%s' has an invalid 'trace' field ('%s'), which lead to a non-existent file ('%s')",
filename.c_str(), profile_name.c_str(), trace.c_str(), string(filename + "/" + trace).c_str());
ifstream traceFile(tracePath.string());
xbt_assert(traceFile.is_open(), "Cannot open file '%s'", tracePath.string().c_str());
string line;
while (std::getline(traceFile, line))
{
trim_right(line);
filesystem::path rank_trace_path(tracePath.parent_path().string() + "/" + line);
data->trace_filenames.push_back("./" + rank_trace_path.string());
}
string filenames = boost::algorithm::join(data->trace_filenames, ", ");
XBT_DEBUG("Filenames of profile '%s': [%s]", profile_name.c_str(), filenames.c_str());
profile->data = data;*/
}
// Let's get the JSON string which describes the profile (to conserve potential fields unused by Batsim)
rapidjson::StringBuffer buffer;
rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);
json_desc.Accept(writer);
profile->json_description = buffer.GetString();
return profile;
}
Profile *Profile::from_json(const std::string & profile_name, const std::string & json_str)
{
Document doc;
doc.Parse(json_str.c_str());
return Profile::from_json(profile_name, doc);
}
......@@ -36,6 +36,26 @@ struct Profile
ProfileType type; //!< The type of the profile
void * data; //!< The associated data
std::string json_description; //!< The JSON description of the profile
/**