diff --git a/cfg/cpu/nosv/breakdown.cfg b/cfg/cpu/nosv/breakdown.cfg new file mode 100644 index 0000000..9ebdb21 --- /dev/null +++ b/cfg/cpu/nosv/breakdown.cfg @@ -0,0 +1,44 @@ +#ParaverCFG +ConfigFile.Version: 3.4 +ConfigFile.NumWindows: 1 + + +################################################################################ +< NEW DISPLAYING WINDOW CPU: nOS-V Runtime/Idle/Task breakdown > +################################################################################ +window_name CPU: nOS-V Runtime/Idle/Task breakdown +window_type single +window_id 1 +window_position_x 0 +window_position_y 0 +window_width 600 +window_height 150 +window_comm_lines_enabled false +window_flags_enabled false +window_noncolor_mode true +window_custom_color_enabled true +window_custom_color_palette {1.000000000000:0,70,0},{3.000000000000:99,131,0},{4.000000000000:53,121,221},{5.000000000000:223,108,0},{6.000000000000:75,127,82},{7.000000000000:242,110,162},{8.000000000000:255,190,0},{9.000000000000:153,114,0},{10.000000000000:156,12,231},{11.000000000000:177,25,229},{12.000000000000:255,72,50},{15.000000000000:0,171,255},{16.000000000000:124,213,228},{17.000000000000:242,239,141},{18.000000000000:80,80,80},{19.000000000000:94,0,0},{20.000000000000:128,165,214},{22.000000000000:222,101,128},{23.000000000000:110,148,255},{100.000000000000:0,100,0},{101.000000000000:100,100,177},{102.000000000000:150,150,0} +window_logical_filtered true +window_physical_filtered false +window_comm_fromto true +window_comm_tagsize true +window_comm_typeval true +window_units Microseconds +window_maximum_y 1000.0 +window_minimum_y 1.0 +window_compute_y_max true +window_level thread +window_scale_relative 1.000000000000 +window_end_time_relative 1.000000000000 +window_object appl { 1, { All } } +window_begin_time_relative 0.000000000000 +window_open true +window_drawmode draw_randnotzero +window_drawmode_rows draw_randnotzero +window_pixel_size 1 +window_labels_to_draw 1 +window_selected_functions { 14, { {cpu, Active Thd}, {appl, Adding}, {task, Adding}, {thread, Last Evt Val}, {node, Adding}, {system, Adding}, {workload, Adding}, {from_obj, All}, {to_obj, All}, {tag_msg, All}, {size_msg, All}, {bw_msg, All}, {evt_type, =}, {evt_value, All} } } +window_compose_functions { 9, { {compose_cpu, As Is}, {compose_appl, As Is}, {compose_task, As Is}, {compose_thread, As Is}, {compose_node, As Is}, {compose_system, As Is}, {compose_workload, As Is}, {topcompose1, As Is}, {topcompose2, As Is} } } +window_filter_module evt_type 1 17 +window_filter_module evt_type_label 1 "CPU: nOS-V Runtime/Idle/Task breakdown" + diff --git a/cfg/cpu/nosv/idle.cfg b/cfg/cpu/nosv/idle.cfg new file mode 100644 index 0000000..af4ef4d --- /dev/null +++ b/cfg/cpu/nosv/idle.cfg @@ -0,0 +1,44 @@ +#ParaverCFG +ConfigFile.Version: 3.4 +ConfigFile.NumWindows: 1 + + +################################################################################ +< NEW DISPLAYING WINDOW CPU: nOS-V idle state of the RUNNING thread > +################################################################################ +window_name CPU: nOS-V idle state of the RUNNING thread +window_type single +window_id 1 +window_position_x 0 +window_position_y 0 +window_width 600 +window_height 150 +window_comm_lines_enabled false +window_flags_enabled false +window_noncolor_mode true +window_custom_color_enabled true +window_custom_color_palette {100.000000000000:0,100,0},{101.000000000000:100,100,177},{102.000000000000:150,150,0} +window_logical_filtered true +window_physical_filtered false +window_comm_fromto true +window_comm_tagsize true +window_comm_typeval true +window_units Microseconds +window_maximum_y 1000.0 +window_minimum_y 1.0 +window_compute_y_max true +window_level thread +window_scale_relative 1.000000000000 +window_end_time_relative 1.000000000000 +window_object appl { 1, { All } } +window_begin_time_relative 0.000000000000 +window_open true +window_drawmode draw_randnotzero +window_drawmode_rows draw_randnotzero +window_pixel_size 1 +window_labels_to_draw 1 +window_selected_functions { 14, { {cpu, Active Thd}, {appl, Adding}, {task, Adding}, {thread, Last Evt Val}, {node, Adding}, {system, Adding}, {workload, Adding}, {from_obj, All}, {to_obj, All}, {tag_msg, All}, {size_msg, All}, {bw_msg, All}, {evt_type, =}, {evt_value, All} } } +window_compose_functions { 9, { {compose_cpu, As Is}, {compose_appl, As Is}, {compose_task, As Is}, {compose_thread, As Is}, {compose_node, As Is}, {compose_system, As Is}, {compose_workload, As Is}, {topcompose1, As Is}, {topcompose2, As Is} } } +window_filter_module evt_type 1 16 +window_filter_module evt_type_label 1 "CPU: nOS-V idle state of the RUNNING thread" + diff --git a/src/emu/CMakeLists.txt b/src/emu/CMakeLists.txt index f10c7ae..a655d84 100644 --- a/src/emu/CMakeLists.txt +++ b/src/emu/CMakeLists.txt @@ -52,6 +52,7 @@ add_library(emu STATIC nanos6/setup.c nanos6/event.c nanos6/breakdown.c + nosv/breakdown.c nosv/setup.c nosv/event.c nodes/setup.c diff --git a/src/emu/emu_prv.h b/src/emu/emu_prv.h index 48b6010..44a5a9f 100644 --- a/src/emu/emu_prv.h +++ b/src/emu/emu_prv.h @@ -19,6 +19,8 @@ enum emu_prv_types { PRV_NOSV_SUBSYSTEM = 13, PRV_NOSV_RANK = 14, PRV_NOSV_BODYID = 15, + PRV_NOSV_IDLE = 16, + PRV_NOSV_BREAKDOWN = 17, PRV_TAMPI_SUBSYSTEM = 20, PRV_MPI_FUNCTION = 25, PRV_NODES_SUBSYSTEM = 30, diff --git a/src/emu/nosv/breakdown.c b/src/emu/nosv/breakdown.c new file mode 100644 index 0000000..2a3d81b --- /dev/null +++ b/src/emu/nosv/breakdown.c @@ -0,0 +1,312 @@ +/* Copyright (c) 2023 Barcelona Supercomputing Center (BSC) + * SPDX-License-Identifier: GPL-3.0-or-later */ + +#include "breakdown.h" +#include +#include +#include "bay.h" +#include "chan.h" +#include "common.h" +#include "cpu.h" +#include "emu.h" +#include "emu_args.h" +#include "emu_prv.h" +#include "extend.h" +#include "model_cpu.h" +#include "mux.h" +#include "nosv_priv.h" +#include "proc.h" +#include "pv/pcf.h" +#include "pv/prf.h" +#include "pv/prv.h" +#include "pv/pvt.h" +#include "recorder.h" +#include "sort.h" +#include "system.h" +#include "task.h" +#include "track.h" +#include "value.h" + + +static int +create_cpu(struct bay *bay, struct nosv_breakdown_cpu *bcpu, int64_t gindex) +{ + enum chan_type t = CHAN_SINGLE; + chan_init(&bcpu->tr, t, "nosv.cpu%ld.breakdown.tr", gindex); + chan_init(&bcpu->tri, t, "nosv.cpu%ld.breakdown.tri", gindex); + + /* Register all channels in the bay */ + if (bay_register(bay, &bcpu->tr) != 0) { + err("bay_register tr failed"); + return -1; + } + if (bay_register(bay, &bcpu->tri) != 0) { + err("bay_register tri failed"); + return -1; + } + + return 0; +} + +int +model_nosv_breakdown_create(struct emu *emu) +{ + if (emu->args.breakdown == 0) + return 0; + + struct nosv_emu *memu = EXT(emu, 'V'); + struct nosv_breakdown_emu *bemu = &memu->breakdown; + + /* Count phy cpus */ + struct system *sys = &emu->system; + int64_t nphycpus = sys->ncpus - sys->nlooms; + bemu->nphycpus = nphycpus; + + /* Create a new Paraver trace */ + struct recorder *rec = &emu->recorder; + bemu->pvt = recorder_add_pvt(rec, "nosv-breakdown", nphycpus); + if (bemu->pvt == NULL) { + err("recorder_add_pvt failed"); + return -1; + } + + if (sort_init(&bemu->sort, &emu->bay, nphycpus, "nosv.breakdown.sort") != 0) { + err("sort_init failed"); + return -1; + } + + for (struct cpu *cpu = sys->cpus; cpu; cpu = cpu->next) { + if (cpu->is_virtual) + continue; + + struct nosv_cpu *mcpu = EXT(cpu, 'V'); + struct nosv_breakdown_cpu *bcpu = &mcpu->breakdown; + + if (create_cpu(&emu->bay, bcpu, cpu->gindex) != 0) { + err("create_cpu failed"); + return -1; + } + } + + return 0; +} + +static int +select_tr(struct mux *mux, struct value value, struct mux_input **input) +{ + /* Only select the task if we are in ST_TASK_BODY and the task_type has + * a non-null value */ + + int64_t in_body = (value.type == VALUE_INT64 && value.i == ST_TASK_BODY); + + if (in_body) { + struct value tt; + struct mux_input *ttinput = mux_get_input(mux, 1); + if (chan_read(ttinput->chan, &tt) != 0) { + err("chan_read failed"); + return -1; + } + + /* Only show task type if we have a task */ + if (tt.type == VALUE_NULL) + in_body = 0; + } + + if (!in_body) { + /* Only select ss if not NULL */ + struct value ss; + struct mux_input *ssinput = mux_get_input(mux, 0); + if (chan_read(ssinput->chan, &ss) != 0) { + err("chan_read failed"); + return -1; + } + + /* Don't select anything, so the default output is shown */ + if (ss.type == VALUE_NULL) { + dbg("not selecting anything"); + *input = NULL; + return 0; + } + } + + int64_t i = in_body; + char *inputs[] = { "subsystem", "task_type" }; + dbg("selecting input %ld (%s)", i, inputs[i]); + *input = mux_get_input(mux, i); + + return 0; +} + +static int +select_idle(struct mux *mux, struct value value, struct mux_input **input) +{ + dbg("selecting tri output for value %s", value_str(value)); + + if (value.type == VALUE_INT64 && value.i == ST_PROGRESSING) { + dbg("selecting input 0 (tr)"); + *input = mux_get_input(mux, 0); + } else { + dbg("selecting input 1 (idle)"); + *input = mux_get_input(mux, 1); + } + + return 0; +} + +static int +connect_cpu(struct bay *bay, struct nosv_cpu *mcpu) +{ + struct nosv_breakdown_cpu *bcpu = &mcpu->breakdown; + + /* Channel aliases */ + struct chan *ss = &mcpu->m.track[CH_SUBSYSTEM].ch; + struct chan *tt = &mcpu->m.track[CH_TYPE].ch; + struct chan *idle = &mcpu->m.track[CH_IDLE].ch; + struct chan *tr = &bcpu->tr; + struct chan *tri = &bcpu->tri; + + /* Connect mux0 using ss as select */ + if (mux_init(&bcpu->mux0, bay, ss, tr, select_tr, 2) != 0) { + err("mux_init failed"); + return -1; + } + + if (mux_set_input(&bcpu->mux0, 0, ss) != 0) { + err("mux_set_input ss failed"); + return -1; + } + + if (mux_set_input(&bcpu->mux0, 1, tt) != 0) { + err("mux_set_input tt failed"); + return -1; + } + + /* TODO what do we emit on null? */ + mux_set_default(&bcpu->mux0, value_int64(666)); + + /* Connect mux 1 using idle as select */ + if (mux_init(&bcpu->mux1, bay, idle, tri, select_idle, 2) != 0) { + err("mux_init failed"); + return -1; + } + + if (mux_set_input(&bcpu->mux1, 0, tr) != 0) { + err("mux_set_input tr failed"); + return -1; + } + + if (mux_set_input(&bcpu->mux1, 1, idle) != 0) { + err("mux_set_input idle failed"); + return -1; + } + + return 0; +} + +int +model_nosv_breakdown_connect(struct emu *emu) +{ + if (emu->args.breakdown == 0) + return 0; + + struct nosv_emu *memu = EXT(emu, 'V'); + struct nosv_breakdown_emu *bemu = &memu->breakdown; + struct bay *bay = &emu->bay; + struct system *sys = &emu->system; + + int64_t i = 0; + for (struct cpu *cpu = sys->cpus; cpu; cpu = cpu->next) { + if (cpu->is_virtual) + continue; + + struct nosv_cpu *mcpu = EXT(cpu, 'V'); + struct nosv_breakdown_cpu *bcpu = &mcpu->breakdown; + + /* Connect tr and tri channels and muxes */ + if (connect_cpu(bay, mcpu) != 0) { + err("connect_cpu failed"); + return -1; + } + + /* Connect tri to sort */ + if (sort_set_input(&bemu->sort, i, &bcpu->tri) != 0) { + err("sort_set_input failed"); + return -1; + } + + /* Connect out to PRV */ + struct prv *prv = pvt_get_prv(bemu->pvt); + long type = PRV_NOSV_BREAKDOWN; + long flags = PRV_SKIPDUP | PRV_ZERO; + + struct chan *out = sort_get_output(&bemu->sort, i); + if (prv_register(prv, i, type, bay, out, flags)) { + err("prv_register failed"); + return -1; + } + + i++; + } + + return 0; +} + +int +model_nosv_breakdown_finish(struct emu *emu, + const struct pcf_value_label **labels) +{ + if (emu->args.breakdown == 0) + return 0; + + struct nosv_emu *memu = EXT(emu, 'V'); + struct nosv_breakdown_emu *bemu = &memu->breakdown; + struct pcf *pcf = pvt_get_pcf(bemu->pvt); + long typeid = PRV_NOSV_BREAKDOWN; + char label[] = "CPU: nOS-V Runtime/Idle/Task breakdown"; + struct pcf_type *pcftype = pcf_add_type(pcf, typeid, label); + const struct pcf_value_label *v = NULL; + + /* Emit subsystem values */ + for (v = labels[CH_SUBSYSTEM]; v->label; v++) { + if (pcf_add_value(pcftype, v->value, v->label) == NULL) { + err("pcf_add_value ss failed"); + return -1; + } + } + + /* Emit idle values */ + for (v = labels[CH_IDLE]; v->label; v++) { + if (pcf_add_value(pcftype, v->value, v->label) == NULL) { + err("pcf_add_value idle failed"); + return -1; + } + } + + /* Emit task_type values */ + struct system *sys = &emu->system; + for (struct proc *p = sys->procs; p; p = p->gnext) { + struct nosv_proc *proc = EXT(p, 'V'); + struct task_info *info = &proc->task_info; + if (task_create_pcf_types(pcftype, info->types) != 0) { + err("task_create_pcf_types failed"); + return -1; + } + } + + /* Also populate the row labels */ + struct prf *prf = pvt_get_prf(bemu->pvt); + for (int64_t row = 0; row < bemu->nphycpus; row++) { + char name[128]; + if (snprintf(name, 128, "~CPU %4ld", bemu->nphycpus - row) >= 128) { + err("label too long"); + return -1; + } + + if (prf_add(prf, row, name) != 0) { + err("prf_add failed for %s", name); + return -1; + } + } + + return 0; +} diff --git a/src/emu/nosv/breakdown.h b/src/emu/nosv/breakdown.h new file mode 100644 index 0000000..376947b --- /dev/null +++ b/src/emu/nosv/breakdown.h @@ -0,0 +1,56 @@ +/* Copyright (c) 2023 Barcelona Supercomputing Center (BSC) + * SPDX-License-Identifier: GPL-3.0-or-later */ + +#ifndef BREAKDOWN_H +#define BREAKDOWN_H + +/* + * The breakdown model is implemented on top of the CPU subsystem, task_type and + * idle channels. The first mux0 selects the task type when the subsystem + * matches "Task body" otherwise forwards the subsystem as-is to tr. The second + * mux1 selects tr only when the CPU is not Idle, otherwise sets the output tri + * as Idle. + * + * +--------+ + * | | + * | v + * | +------+ + * subsystem -+-->--| | + * | mux0 | +------+ + * task_type ---->--| |-->-- tr -->--| | + * +------+ | mux1 |-->-- tri + * idle --------->-------------------+->--| | + * | +------+ + * | ^ + * | | + * +--------+ + * + * Then the sort module takes the output tri of each CPU and sorts the values + * which are propagated to the PRV directly. + * + * +------+ +-----+ + * cpu0.tri --->---| |--->---| | + * ... | sort | ... | PRV | + * cpuN.tri --->---| |--->---| | + * +------+ +-----+ + */ + +#include +#include "chan.h" +#include "mux.h" +#include "sort.h" + +struct nosv_breakdown_cpu { + struct mux mux0; + struct chan tr; + struct mux mux1; + struct chan tri; +}; + +struct nosv_breakdown_emu { + int64_t nphycpus; + struct sort sort; + struct pvt *pvt; +}; + +#endif /* BREAKDOWN_H */ diff --git a/src/emu/nosv/event.c b/src/emu/nosv/event.c index 2d79466..9e47fae 100644 --- a/src/emu/nosv/event.c +++ b/src/emu/nosv/event.c @@ -16,7 +16,7 @@ #include "thread.h" #include "value.h" -enum { PUSH = 1, POP = 2, IGN = 3 }; +enum { PUSH = 1, POP = 2, SET = 3, IGN = 4 }; #define CHSS CH_SUBSYSTEM @@ -77,6 +77,11 @@ static const int ss_table[256][256][3] = { ['d'] = { CHSS, PUSH, ST_DELEGATE }, ['D'] = { CHSS, POP, ST_DELEGATE }, }, + ['P'] = { + ['p'] = { CH_IDLE, SET, ST_PROGRESSING }, + ['r'] = { CH_IDLE, SET, ST_RESTING }, + ['a'] = { CH_IDLE, SET, ST_ABSORBING }, + }, }; static int @@ -94,6 +99,8 @@ simple(struct emu *emu) return chan_push(ch, value_int64(st)); } else if (action == POP) { return chan_pop(ch, value_int64(st)); + } else if (action == SET) { + return chan_set(ch, value_int64(st)); } else if (action == IGN) { return 0; /* do nothing */ } else { @@ -574,6 +581,7 @@ process_ev(struct emu *emu) case 'M': case 'H': case 'A': + case 'P': return simple(emu); case 'T': return pre_task(emu); diff --git a/src/emu/nosv/nosv_priv.h b/src/emu/nosv/nosv_priv.h index 67e7b23..29edd61 100644 --- a/src/emu/nosv/nosv_priv.h +++ b/src/emu/nosv/nosv_priv.h @@ -4,6 +4,7 @@ #ifndef NOSV_PRIV_H #define NOSV_PRIV_H +#include "breakdown.h" #include "emu.h" #include "task.h" #include "model_cpu.h" @@ -18,6 +19,7 @@ enum nosv_chan { CH_APPID, CH_SUBSYSTEM, CH_RANK, + CH_IDLE, CH_MAX, }; @@ -56,16 +58,36 @@ struct nosv_thread { struct nosv_cpu { struct model_cpu m; + struct nosv_breakdown_cpu breakdown; }; struct nosv_proc { struct task_info task_info; }; +struct nosv_emu { + int connected; + int event; + struct nosv_breakdown_emu breakdown; +}; + +enum nosv_progress { + /* Can mix with subsystem values */ + ST_PROGRESSING = 100, + ST_RESTING, + ST_ABSORBING, +}; + int model_nosv_probe(struct emu *emu); int model_nosv_create(struct emu *emu); int model_nosv_connect(struct emu *emu); int model_nosv_event(struct emu *emu); int model_nosv_finish(struct emu *emu); +int model_nosv_breakdown_create(struct emu *emu); +int model_nosv_breakdown_connect(struct emu *emu); +int model_nosv_breakdown_finish(struct emu *emu, + const struct pcf_value_label **labels); + + #endif /* NOSV_PRIV_H */ diff --git a/src/emu/nosv/setup.c b/src/emu/nosv/setup.c index fa9d908..b4eaf5c 100644 --- a/src/emu/nosv/setup.c +++ b/src/emu/nosv/setup.c @@ -5,6 +5,7 @@ #include #include #include "chan.h" +#include "cpu.h" #include "common.h" #include "emu.h" #include "emu_args.h" @@ -96,6 +97,7 @@ static const char *chan_name[CH_MAX] = { [CH_APPID] = "appid", [CH_SUBSYSTEM] = "subsystem", [CH_RANK] = "rank", + [CH_IDLE] = "idle", }; static const int chan_stack[CH_MAX] = { @@ -121,6 +123,7 @@ static const int pvt_type[CH_MAX] = { [CH_APPID] = PRV_NOSV_APPID, [CH_SUBSYSTEM] = PRV_NOSV_SUBSYSTEM, [CH_RANK] = PRV_NOSV_RANK, + [CH_IDLE] = PRV_NOSV_IDLE, }; static const char *pcf_prefix[CH_MAX] = { @@ -130,6 +133,7 @@ static const char *pcf_prefix[CH_MAX] = { [CH_APPID] = "nOS-V task AppID", [CH_SUBSYSTEM] = "nOS-V subsystem", [CH_RANK] = "nOS-V task MPI rank", + [CH_IDLE] = "nOS-V idle state", }; static const struct pcf_value_label nosv_ss_values[] = { @@ -160,8 +164,16 @@ static const struct pcf_value_label nosv_ss_values[] = { { -1, NULL }, }; +static const struct pcf_value_label nosv_worker_idle[] = { + { ST_PROGRESSING, "Progressing" }, + { ST_RESTING, "Resting" }, + { ST_ABSORBING, "Absorbing noise" }, + { -1, NULL }, +}; + static const struct pcf_value_label *pcf_labels[CH_MAX] = { [CH_SUBSYSTEM] = nosv_ss_values, + [CH_IDLE] = nosv_worker_idle, }; static const long prv_flags[CH_MAX] = { @@ -171,6 +183,7 @@ static const long prv_flags[CH_MAX] = { [CH_APPID] = PRV_SKIPDUPNULL, /* Switch to task of same appid */ [CH_SUBSYSTEM] = PRV_SKIPDUPNULL, [CH_RANK] = PRV_SKIPDUPNULL, /* Switch to task of same rank */ + [CH_IDLE] = PRV_SKIPDUPNULL, }; static const struct model_pvt_spec pvt_spec = { @@ -189,6 +202,7 @@ static const int th_track[CH_MAX] = { [CH_APPID] = TRACK_TH_RUN, [CH_SUBSYSTEM] = TRACK_TH_ACT, [CH_RANK] = TRACK_TH_RUN, + [CH_IDLE] = TRACK_TH_RUN, }; static const int cpu_track[CH_MAX] = { @@ -198,6 +212,7 @@ static const int cpu_track[CH_MAX] = { [CH_APPID] = TRACK_TH_RUN, [CH_SUBSYSTEM] = TRACK_TH_RUN, [CH_RANK] = TRACK_TH_RUN, + [CH_IDLE] = TRACK_TH_RUN, }; /* ----------------- chan_spec ------------------ */ @@ -279,6 +294,19 @@ model_nosv_create(struct emu *emu) } } + struct nosv_emu *e = calloc(1, sizeof(struct nosv_emu)); + if (e == NULL) { + err("calloc failed:"); + return -1; + } + + extend_set(&emu->ext, model_id, e); + + if (model_nosv_breakdown_create(emu) != 0) { + err("model_nosv_breakdown_create failed"); + return -1; + } + return 0; } @@ -295,6 +323,28 @@ model_nosv_connect(struct emu *emu) return -1; } + if (emu->args.breakdown && model_nosv_breakdown_connect(emu) != 0) { + err("model_nosv_breakdown_connect failed"); + return -1; + } + + for (struct thread *th = emu->system.threads; th; th = th->gnext) { + struct nosv_thread *mth = EXT(th, model_id); + struct chan *idle = &mth->m.ch[CH_IDLE]; + /* By default set all threads as Progressing */ + if (chan_set(idle, value_int64(ST_PROGRESSING)) != 0) { + err("chan_push idle failed"); + return -1; + } + } + + for (struct cpu *cpu = emu->system.cpus; cpu; cpu = cpu->next) { + struct nosv_cpu *mcpu = EXT(cpu, model_id); + struct mux *mux = &mcpu->m.track[CH_IDLE].mux; + /* Emit Resting when a CPU has no running threads */ + mux_set_default(mux, value_int64(ST_RESTING)); + } + return 0; } @@ -343,6 +393,10 @@ finish_pvt(struct emu *emu, const char *name) struct pcf *pcf = pvt_get_pcf(pvt); long typeid = pvt_type[CH_TYPE]; struct pcf_type *pcftype = pcf_find_type(pcf, typeid); + if (pcftype == NULL) { + err("cannot find %s pcf type %d", name, typeid); + return -1; + } for (struct proc *p = sys->procs; p; p = p->gnext) { struct nosv_proc *proc = EXT(p, model_id); @@ -370,6 +424,11 @@ model_nosv_finish(struct emu *emu) return -1; } + if (model_nosv_breakdown_finish(emu, pcf_labels) != 0) { + err("model_nosv_breakdown_finish failed"); + return -1; + } + /* When running in linter mode perform additional checks */ if (emu->args.linter_mode && end_lint(emu) != 0) { err("end_lint failed");