VMM OpenSource - sv/perf/vmm_perf_analyzer.sv

sv/perf/vmm_perf_analyzer.sv expanded source

00001 //
00002 // -------------------------------------------------------------
00003 //    Copyright 2004-2008 Synopsys, Inc.
00004 //    All Rights Reserved Worldwide
00005 // 
00006 //    Licensed under the Apache License, Version 2.0 (the
00007 //    "License"); you may not use this file except in
00008 //    compliance with the License.  You may obtain a copy of
00009 //    the License at
00010 // 
00011 //        http://www.apache.org/licenses/LICENSE-2.0
00012 // 
00013 //    Unless required by applicable law or agreed to in
00014 //    writing, software distributed under the License is
00015 //    distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
00016 //    CONDITIONS OF ANY KIND, either express or implied.  See
00017 //    the License for the specific language governing
00018 //    permissions and limitations under the License.
00019 // -------------------------------------------------------------
00020 //
00021 
00022 
00023 `ifndef VMM_PERF_ANALYZER__SV
00024 `define VMM_PERF_ANALYZER__SV
00025 
00026 `include "vmm.sv"
00027 `include "perf/vmm_perf_tenure.sv"
00028 `include "perf/vmm_sql.sv"
00029 
00030 class vmm_perf_data;
00031    time start_time;
00032    time end_time;
00033    time resume_time;
00034    time active_time = 0;
00035    string more_data;
00036 
00037    bit  aborted;
00038 
00039    vmm_perf_tenure tenure;
00040 endclass: vmm_perf_data
00041 
00042 
00043 typedef class vmm_perf_analyzer;
00044 
00045 
00046 class vmm_perf_analyzer_callbacks;
00047    virtual function void analyze_tenure(vmm_perf_analyzer analyzer,
00048                                         vmm_perf_tenure   tenure,
00049                                         ref time          start_time,
00050                                         ref time          end_time,
00051                                         ref time          active_time,
00052                                         ref bit           aborted,
00053                                         ref string        more_data,
00054                                         ref bit           filtered);
00055    endfunction
00056 endclass
00057 
00058 
00059 class vmm_perf_analyzer;
00060 
00061    typedef enum {SOFT, HARD} reset_e;
00062 
00063    vmm_log log;
00064 
00065    local int unsigned max_n_initiators;
00066    local int unsigned max_n_targets;
00067    local int unsigned max_n_concurrent;
00068 
00069    local int unsigned known_initiators[*];
00070    local int unsigned known_targets[*];
00071 
00072    local vmm_perf_data active[$];
00073    local vmm_perf_data suspended[$];
00074    local vmm_perf_data completed[$];
00075 
00076    local vmm_sql_table sql_table;
00077    local string        user_schema;
00078 
00079    local vmm_perf_analyzer_callbacks cbs[$];
00080 
00081    // For simple report
00082    local time min_time;
00083    local time max_time;
00084    local time active_time;
00085    local time min_abort_time;
00086    local time max_abort_time;
00087    local time abort_time;
00088    local int  n;
00089 
00090    extern function new(string       name             = "",
00091                        vmm_sql_db   sql_db,
00092                        int unsigned max_n_initiators = 0,
00093                        int unsigned max_n_targets    = 0,
00094                        int unsigned max_n_concurrent = 1,
00095                        string user_schema = "");
00096 
00097    extern virtual function time now();
00098 
00099    extern function void start_tenure(vmm_perf_tenure tenure);
00100    extern function void suspend_tenure(vmm_perf_tenure tenure);
00101    extern function void resume_tenure(vmm_perf_tenure tenure);
00102    extern function void end_tenure(vmm_perf_tenure tenure,
00103                                    string more_data = "");
00104    extern function void abort_tenure(vmm_perf_tenure tenure,
00105                                      string more_data = "");
00106    extern function bit add_tenure(int      initiator_id = -1,
00107                                   int      target_id    = -1,
00108                                   time     start_time,
00109                                   time     end_time,
00110                                   vmm_data tr           = null,
00111                                   time     active_time  = 0,
00112                                   bit      aborted      = 0,
00113                                   string   more_data    = "");
00114 
00115    extern virtual function string psdisplay(string prefix = "");
00116    extern function void report(string name         = "",
00117                                bit    brief        = 1);
00118 
00119    extern function vmm_sql_db get_db();
00120    extern function void save_db();
00121    extern function void save_db_txt(string name = "");
00122 
00123    extern function void reset(reset_e rst_typ = SOFT);
00124 
00125    extern function void append_callback(vmm_perf_analyzer_callbacks cb);
00126    extern function void prepend_callback(vmm_perf_analyzer_callbacks cb);
00127    extern function void unregister_callback(vmm_perf_analyzer_callbacks cb);
00128 endclass: vmm_perf_analyzer
00129 
00130 function vmm_perf_analyzer::new(string       name,
00131                                 vmm_sql_db   sql_db,
00132                                 int unsigned max_n_initiators,
00133                                 int unsigned max_n_targets,
00134                                 int unsigned max_n_concurrent,
00135                                 string user_schema);
00136    string schema;
00137 
00138    this.log = new("Performance Analyzer", name);
00139 
00140    this.max_n_initiators = max_n_initiators;
00141    this.max_n_targets = max_n_targets;
00142 
00143    this.max_n_concurrent = max_n_concurrent;
00144    if (this.max_n_concurrent == 0) this.max_n_concurrent = (1<<31)-1;
00145 
00146    schema = "tenureID integer, initiatorID integer, targetID integer, start bigint, end bigint, active bigint, Abort tinyint";
00147    this.user_schema = user_schema;
00148    if (user_schema != "") schema = {schema, ", ", user_schema};
00149    this.sql_table = sql_db.create_table(name, schema, 0);
00150 
00151    this.active_time = 0;
00152    this.abort_time = 0;
00153    this.max_time = 0;
00154    this.min_time = '1;
00155    this.max_abort_time = 0;
00156    this.min_abort_time = '1;
00157    this.n = 0;
00158 endfunction: new
00159 
00160 
00161 function time vmm_perf_analyzer::now();
00162    return $time();
00163 endfunction: now
00164 
00165 
00166 function void vmm_perf_analyzer::start_tenure(vmm_perf_tenure tenure);
00167    vmm_perf_data datum;
00168 
00169    if (tenure == null) begin
00170       `vmm_error(this.log, "Attempting to start a NULL tenure");
00171       return;
00172    end
00173 
00174    foreach (this.active[i]) begin
00175       if (this.active[i].tenure == tenure) begin
00176          `vmm_error(this.log, {"Attempting to start an already-active tenure: ",
00177                                tenure.psdisplay()});
00178          return;
00179       end
00180    end
00181 
00182    if (this.max_n_initiators > 0) begin
00183       if (!this.known_initiators.exists(tenure.get_initiator_id())) begin
00184          this.known_initiators[tenure.get_initiator_id()] = 1;
00185          if (this.known_initiators.num() > this.max_n_initiators) begin
00186             `vmm_error(this.log, $psprintf("Too many initiators: %0d > %0d",
00187                                            this.known_initiators.num(),
00188                                            this.max_n_initiators));
00189          end
00190       end
00191    end
00192 
00193    if (this.max_n_targets > 0) begin
00194       if (!this.known_targets.exists(tenure.get_initiator_id())) begin
00195          this.known_targets[tenure.get_target_id()] = 1;
00196          if (this.known_targets.num() > this.max_n_targets) begin
00197             `vmm_error(this.log, $psprintf("Too many targets: %0d > %0d",
00198                                            this.known_targets.num(),
00199                                            this.max_n_targets));
00200          end
00201       end
00202    end
00203 
00204    datum = new;
00205    datum.tenure = tenure;
00206 
00207    datum.start_time = this.now();
00208    datum.resume_time = datum.start_time;
00209 
00210    this.active.push_back(datum);
00211 
00212    if (this.active.size() > this.max_n_concurrent) begin
00213       `vmm_error(this.log, $psprintf("Too many active tenures: %0d > %0d",
00214                                      this.active.size(), this.max_n_concurrent));
00215       foreach (this.active[i]) begin
00216          `vmm_debug(this.log, this.active[i].tenure.psdisplay("Active: "));
00217       end
00218    end
00219 endfunction: start_tenure
00220 
00221 
00222 function void vmm_perf_analyzer::suspend_tenure(vmm_perf_tenure tenure);
00223    if (tenure == null) begin
00224       `vmm_error(this.log, "Attempting to suspend a NULL tenure");
00225       return;
00226    end
00227 
00228    foreach (this.active[i]) begin
00229       if (this.active[i].tenure == tenure) begin
00230          vmm_perf_data datum = this.active[i];
00231 
00232          this.active.delete(i);
00233          this.suspended.push_back(datum);
00234          datum.active_time += this.now() - datum.resume_time;
00235 
00236          return;
00237       end
00238    end
00239 
00240    `vmm_error(this.log, {"Attempting to suspend a non-active tenure: ",
00241                          tenure.psdisplay()});
00242 endfunction: suspend_tenure
00243 
00244 
00245 function void vmm_perf_analyzer::resume_tenure(vmm_perf_tenure tenure);
00246    if (tenure == null) begin
00247       `vmm_error(this.log, "Attempting to resume a NULL tenure");
00248       return;
00249    end
00250 
00251    foreach (this.suspended[i]) begin
00252       if (this.suspended[i].tenure == tenure) begin
00253          vmm_perf_data datum = this.suspended[i];
00254 
00255          this.suspended.delete(i);
00256          this.active.push_back(datum);
00257 
00258          datum.resume_time = datum.start_time;
00259 
00260          if (this.active.size() > this.max_n_concurrent) begin
00261             `vmm_error(this.log, $psprintf("Too many active tenures: %0d > %0d",
00262                                            this.active.size(), this.max_n_concurrent));
00263          end
00264          return;
00265       end
00266    end
00267    `vmm_error(this.log, {"Attempting to resume a non-suspended tenure: ",
00268                          tenure.psdisplay()});
00269 endfunction: resume_tenure
00270 
00271 
00272 function void vmm_perf_analyzer::end_tenure(vmm_perf_tenure tenure, 
00273                                             string          more_data);
00274    if (tenure == null) begin
00275       `vmm_error(this.log, "Attempting to end a NULL tenure");
00276       return;
00277    end
00278 
00279    foreach (this.active[i]) begin
00280       if (this.active[i].tenure == tenure) begin
00281          vmm_perf_data datum = this.active[i];
00282          bit filtered = 0;
00283 
00284          this.active.delete(i);
00285 
00286          datum.end_time = this.now();
00287          datum.active_time += this.now() - datum.resume_time;
00288          datum.aborted = 0;
00289          datum.more_data = more_data;
00290 
00291          foreach (this.cbs[i]) begin
00292             this.cbs[i].analyze_tenure(this,
00293                                        datum.tenure,
00294                                        datum.start_time,
00295                                        datum.end_time,
00296                                        datum.active_time,
00297                                        datum.aborted,
00298                                        datum.more_data,
00299                                        filtered);
00300          end
00301 
00302          if (filtered) return;
00303 
00304          if (datum.end_time < datum.start_time) begin
00305             `vmm_warning(this.log, $psprintf("Tenure modified with end time (%0d) < start time (%0d)", datum.end_time, datum.start_time));
00306             return;
00307          end
00308 
00309          if (datum.active_time > datum.end_time - datum.start_time) begin
00310             `vmm_error(this.log, $psprintf("Tenure modified with active time (%0d) > end time - start time (%0d)", datum.active_time, datum.end_time - datum.start_time));
00311             return;
00312          end
00313 
00314          this.completed.push_back(datum);
00315 
00316          this.active_time += datum.active_time;
00317          if (this.min_time > datum.active_time) this.min_time = datum.active_time;
00318          if (this.max_time < datum.active_time) this.max_time = datum.active_time;
00319          this.n++;
00320 
00321          return;
00322       end
00323    end
00324 
00325    `vmm_error(this.log, {"Attempting to end a non-active tenure: ",
00326                          tenure.psdisplay()});
00327 endfunction: end_tenure
00328 
00329 
00330 function void vmm_perf_analyzer::abort_tenure(vmm_perf_tenure tenure,
00331                                               string          more_data);
00332    if (tenure == null) begin
00333       `vmm_error(this.log, "Attemtping to abort a NULL tenure");
00334       return;
00335    end
00336 
00337    foreach (this.active[i]) begin
00338       if (this.active[i].tenure == tenure) begin
00339          vmm_perf_data datum = this.active[i];
00340          bit filtered = 0;
00341 
00342          this.active.delete(i);
00343 
00344          datum.end_time = this.now();
00345          datum.active_time += this.now() - datum.resume_time;
00346          datum.aborted = 1;
00347          datum.more_data = more_data;
00348 
00349          foreach (this.cbs[i]) begin
00350             this.cbs[i].analyze_tenure(this,
00351                                        datum.tenure,
00352                                        datum.start_time,
00353                                        datum.end_time,
00354                                        datum.active_time,
00355                                        datum.aborted,
00356                                        datum.more_data,
00357                                        filtered);
00358          end
00359 
00360          if (filtered) return;
00361 
00362          if (datum.end_time < datum.start_time) begin
00363             `vmm_warning(this.log, $psprintf("Tenure modified with end time (%0d) < start time (%0d)", datum.end_time, datum.start_time));
00364             return;
00365          end
00366 
00367          if (datum.active_time > datum.end_time - datum.start_time) begin
00368             `vmm_error(this.log, $psprintf("Tenure modified with active time (%0d) > end time - start time (%0d)", datum.active_time, datum.end_time - datum.start_time));
00369             return;
00370          end
00371 
00372          this.completed.push_back(datum);
00373 
00374          this.active_time += datum.active_time;
00375          if (this.min_time > datum.active_time) this.min_time = datum.active_time;
00376          if (this.max_time < datum.active_time) this.max_time = datum.active_time;
00377 
00378          this.abort_time += datum.active_time;
00379          if (this.min_abort_time > datum.active_time) this.min_abort_time = datum.active_time;
00380          if (this.max_abort_time < datum.active_time) this.max_abort_time = datum.active_time;
00381          this.n++;
00382 
00383          return;
00384       end
00385    end
00386 
00387    `vmm_error(this.log, {"Attempting to abort a non-active tenure: ",
00388                          tenure.psdisplay()});
00389 endfunction: abort_tenure
00390 
00391 
00392 function bit vmm_perf_analyzer::add_tenure(int      initiator_id,
00393                                            int      target_id,
00394                                            time     start_time,
00395                                            time     end_time,
00396                                            vmm_data tr,
00397                                            time     active_time,
00398                                            bit      aborted,
00399                                            string   more_data);
00400    if (end_time < start_time) begin
00401       `vmm_error(this.log, $psprintf("Attempting to add tenure with end time (%0d) < start time (%0d)", end_time, start_time));
00402       return 0;
00403    end
00404 
00405    if (active_time > end_time - start_time) begin
00406       `vmm_error(this.log, $psprintf("Attempting to add tenure with active time (%0d) > end time - start time (%0d)", active_time, end_time - start_time));
00407       return 0;
00408    end
00409 
00410    if (active_time == 0) active_time = end_time - start_time;
00411 
00412    begin
00413       vmm_perf_tenure tenure = new(initiator_id, target_id, tr);
00414       bit             filtered = 0;
00415 
00416       foreach (this.cbs[i]) begin
00417          this.cbs[i].analyze_tenure(this,
00418                                     tenure,
00419                                     start_time,
00420                                     end_time,
00421                                     active_time,
00422                                     aborted,
00423                                     more_data,
00424                                     filtered);
00425       end
00426 
00427       if (filtered) return 1;
00428 
00429       if (end_time < start_time) begin
00430          `vmm_error(this.log, $psprintf("Tenure modified with end time (%0d) < start time (%0d)", end_time, start_time));
00431          return 0;
00432       end
00433 
00434       if (active_time > end_time - start_time) begin
00435          `vmm_error(this.log, $psprintf("Tenure modified with active time (%0d) > end time - start time (%0d)", active_time, end_time - start_time));
00436          return 0;
00437       end
00438 
00439       begin
00440          vmm_perf_data   datum = new;
00441 
00442          datum.tenure      = tenure;
00443          datum.start_time  = start_time;
00444          datum.end_time    = end_time;
00445          datum.active_time = active_time;
00446          datum.aborted     = aborted;
00447          datum.more_data   = more_data;
00448 
00449          this.completed.push_back(datum);
00450 
00451          this.active_time += datum.active_time;
00452          if (this.min_time > datum.active_time) this.min_time = datum.active_time;
00453          if (this.max_time < datum.active_time) this.max_time = datum.active_time;
00454 
00455          if (aborted) begin
00456             this.abort_time += datum.active_time;
00457             if (this.min_abort_time > datum.active_time) this.min_abort_time = datum.active_time;
00458             if (this.max_abort_time < datum.active_time) this.max_abort_time = datum.active_time;
00459          end
00460          this.n++;
00461 
00462       end
00463    end
00464 
00465    return 1;
00466 endfunction: add_tenure
00467 
00468 
00469 function string vmm_perf_analyzer::psdisplay(string prefix);
00470    $sformat(psdisplay, "%s-- Performance Analyzer Status --", prefix);
00471    $sformat(psdisplay, "%s\n%sMax init/targ/conc = %0d/%0d/%0d",
00472             psdisplay, prefix, this.max_n_initiators, this.max_n_targets,
00473             this.max_n_concurrent);
00474    $sformat(psdisplay, "%s\n%sActive tenures:", psdisplay, prefix);
00475    foreach (this.active[i]) begin
00476       $sformat(psdisplay, "%s\n%s", psdisplay, this.active[i].tenure.psdisplay({prefix, "   "}));
00477    end
00478    $sformat(psdisplay, "%s\n%sSuspended tenures:", psdisplay, prefix);
00479    foreach (this.suspended[i]) begin
00480       $sformat(psdisplay, "%s\n%s", psdisplay, this.suspended[i].tenure.psdisplay({prefix, "   "}));
00481    end
00482 endfunction: psdisplay
00483 
00484 
00485 function void vmm_perf_analyzer::report(string name,
00486                                         bit    brief);
00487    int fp = 32'h8000_0001; // STDOUT
00488 
00489    if (name != "") begin
00490       fp = $fopen(name, "w");
00491       if (!fp) begin
00492          `vmm_error(this.log, {"Unable to open ", name, " for writing."});
00493          return;
00494       end
00495    end
00496    
00497    $fwrite(fp, "\nPerformance Analysis Report for %s (based on %0d data points)\n", this.log.get_instance(), n);
00498    $fwrite(fp, "====================================================================\n");
00499    $fwrite(fp, "                            All Completed  |              Aborted\n");
00500 
00501    $fwrite(fp, "Min Tenure Duration: %d  | %d\n",
00502            this.min_time, this.min_abort_time);
00503    $fwrite(fp, "Avg Tenure Duration: %d  | %d\n",
00504            this.active_time / n, this.abort_time / n);
00505    $fwrite(fp, "Max Tenure Duration: %d  | %d\n",
00506            this.max_time, this.max_abort_time);
00507    $fwrite(fp, "Utilization        : %d%% | %d%%\n",
00508            (this.active_time * 100) / this.now(),
00509            (this.abort_time * 100) / this.now());
00510 
00511    if (name != "") begin
00512       $fclose(fp);
00513    end
00514 
00515    if (this.active.size() != 0 ||
00516        this.suspended.size() != 0) begin
00517       `vmm_warning(this.log, $psprintf("There are %0d active/suspended tenures not included in the report",
00518                    this.active.size() + this.suspended.size()));
00519    end
00520 endfunction: report
00521 
00522 function vmm_sql_db vmm_perf_analyzer::get_db();
00523    return this.sql_table.get_db();
00524 endfunction: get_db
00525 
00526 function void vmm_perf_analyzer::save_db();
00527    foreach (this.completed[i]) begin
00528       vmm_perf_data   datum  = this.completed[i];
00529       vmm_perf_tenure tenure = datum.tenure;
00530       string row;
00531 
00532       $sformat(row, "%0d,%0d,%0d,%0d,%0d,%0d,%b",
00533 	       tenure.get_tenure_id(),
00534 	       tenure.get_initiator_id(), 
00535 	       tenure.get_target_id(),
00536 	       datum.start_time, 
00537 	       datum.end_time, 
00538 	       datum.active_time, 
00539 	       datum.aborted);
00540 
00541       if (this.user_schema != "") row = {row, ",", datum.more_data};
00542 
00543       this.sql_table.insert(row);
00544    end
00545    this.completed.delete();
00546 
00547    begin
00548       vmm_sql_db sql_db = this.sql_table.get_db();
00549       sql_db.commit();
00550    end
00551 
00552    if (this.active.size() != 0 ||
00553        this.suspended.size() != 0) begin
00554       `vmm_warning(this.log, $psprintf("There are %0d active/suspended tenures not included in the report",
00555                    this.active.size() + this.suspended.size()));
00556    end
00557 endfunction: save_db
00558 
00559 function void vmm_perf_analyzer::save_db_txt(string name);
00560    int fp;
00561 
00562    if (name == "") begin
00563       name = {this.log.get_instance(), ".perf.db"};
00564    end
00565    
00566    fp = $fopen(name, "w");
00567    if (!fp) begin
00568       `vmm_error(this.log, {"Unable to open ", name, " for writing."});
00569       return;
00570    end
00571 
00572    $fwrite("TenureID InitiatorID TargetID Start End Active Abort\n");
00573    foreach (this.completed[i]) begin
00574       vmm_perf_data   datum  = this.completed[i];
00575       vmm_perf_tenure tenure = datum.tenure;
00576 
00577       $fwrite(fp, "%0d %0d %0d %0d %0d %0d %b\n", tenure.get_tenure_id(),
00578               tenure.get_initiator_id(), tenure.get_target_id(),
00579               datum.start_time, datum.end_time, datum.active_time, datum.aborted);
00580    end
00581 
00582    $fclose(fp);
00583 
00584    if (this.active.size() != 0 ||
00585        this.suspended.size() != 0) begin
00586       `vmm_warning(this.log, $psprintf("There are %0d active/suspended tenures not included in the DB",
00587                    this.active.size() + this.suspended.size()));
00588    end
00589 endfunction: save_db_txt
00590 
00591 function void vmm_perf_analyzer::reset(reset_e rst_typ);
00592    this.known_initiators.delete();
00593    this.known_targets.delete();
00594    this.active.delete();
00595    this.completed.delete();
00596    begin
00597      vmm_sql_db sql_db = this.sql_table.get_db();
00598      sql_db.statement(
00599          $psprintf(
00600              "DELETE FROM TABLE %s;",
00601              this.sql_table.get_tblname)
00602          );
00603    end
00604 
00605    this.active_time = 0;
00606    this.abort_time = 0;
00607    this.max_time = 0;
00608    this.min_time = '1;
00609    this.max_abort_time = 0;
00610    this.min_abort_time = '1;
00611    this.n = 0;
00612 
00613    if (rst_typ == HARD) begin
00614       this.cbs.delete();
00615    end
00616 endfunction: reset
00617 
00618 
00619 function void vmm_perf_analyzer::append_callback(vmm_perf_analyzer_callbacks cb);
00620    foreach (this.cbs[i]) begin
00621       if (this.cbs[i] == cb) begin
00622          `vmm_warning(this.log, "Callback has already been registered");
00623          return;
00624       end
00625    end
00626    
00627    // Append new callback
00628    this.cbs.push_back(cb);
00629 endfunction: append_callback
00630 
00631 
00632 function void vmm_perf_analyzer::prepend_callback(vmm_perf_analyzer_callbacks cb);
00633    foreach (this.cbs[i]) begin
00634       if (this.cbs[i] == cb) begin
00635          `vmm_warning(this.log, "Callback has already been registered");
00636          return;
00637       end
00638    end
00639    
00640    // Prepend new callback
00641    this.cbs.push_front(cb);
00642 endfunction: prepend_callback
00643 
00644 
00645 function void vmm_perf_analyzer::unregister_callback(vmm_perf_analyzer_callbacks cb);
00646    foreach (this.cbs[i]) begin
00647       if (this.cbs[i] == cb) begin
00648          int j = i;
00649          // Unregister it
00650          this.cbs.delete(j);
00651          return;
00652       end
00653    end
00654 
00655    `vmm_warning(this.log, "Callback was not registered");
00656 endfunction: unregister_callback
00657 
00658 `endif // VMM_PERF_ANALYZER__SV