/* Copyright (C) 2021-2023 Free Software Foundation, Inc. Contributed by Oracle. This file is part of GNU Binutils. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #include "DbeSession.h" #include "FileData.h" #include "StringBuilder.h" #include "i18n.h" #include "util.h" #include "IOActivity.h" #include "MetricList.h" #include "Application.h" #include "Experiment.h" #include "DbeView.h" #include "Exp_Layout.h" #include "i18n.h" IOActivity::IOActivity (DbeView *_dbev) { dbev = _dbev; fDataHash = NULL; fDataTotal = NULL; fDataObjs = NULL; fDataObjsFile = NULL; hasFile = false; fDataObjsVfd = NULL; hasVfd = false; fDataObjsCallStack = NULL; hasCallStack = false; fDataCalStkMap = NULL; fDataVfdMap = NULL; hist_data_file_all = NULL; hist_data_vfd_all = NULL; hist_data_callstack_all = NULL; } void IOActivity::reset () { int numExps = dbeSession->nexps (); FileData *fData = NULL; DefaultMap* fDataMap; for (int k = 0; k < numExps; k++) { Experiment *exp = dbeSession->get_exp (k); fDataMap = exp->getFDataMap (); if (fDataMap == NULL) continue; fDataObjs = fDataMap->values (); if (fDataObjs == NULL) continue; int numFiles = fDataObjs->size (); for (int j = 0; j < numFiles; j++) { fData = fDataObjs->fetch (j); fData->init (); } } delete fDataHash; fDataHash = NULL; delete fDataTotal; fDataTotal = NULL; delete fDataObjsFile; fDataObjsFile = NULL; hasFile = false; delete fDataObjsVfd; fDataObjsVfd = NULL; hasVfd = false; delete fDataObjsCallStack; fDataObjsCallStack = NULL; hasCallStack = false; delete fDataObjs; fDataObjs = NULL; delete fDataCalStkMap; fDataCalStkMap = NULL; delete fDataVfdMap; fDataVfdMap = NULL; // These three pointers are deleted by DbeView // They are named iofile_data, iovfd_data, and iocs_data hist_data_file_all = NULL; hist_data_vfd_all = NULL; hist_data_callstack_all = NULL; } void IOActivity::createHistItemTotals (Hist_data *hist_data, MetricList *mlist, Histable::Type hType, bool empty) { int mIndex; Metric *mtr; Hist_data::HistItem *hi; FileData *fData = NULL; if (fDataTotal == NULL) { fDataTotal = new FileData (TOTAL_FILENAME); fDataTotal->setHistType (hType); fDataTotal->setVirtualFd (VIRTUAL_FD_TOTAL); fDataTotal->id = 0; } fData = new FileData (fDataTotal); fData->setHistType (hType); hi = hist_data->append_hist_item (fData); Vec_loop (Metric *, mlist->get_items (), mIndex, mtr) { if (!mtr->is_visible () && !mtr->is_tvisible () && !mtr->is_pvisible ()) continue; Metric::Type mtype = mtr->get_type (); ValueTag vType = mtr->get_vtype (); hist_data->total->value[mIndex].tag = vType; hi->value[mIndex].tag = vType; double prec = (double) NANOSEC; switch (mtype) { case BaseMetric::IO_READ_BYTES: if (!empty) { hist_data->total->value[mIndex].ll = fDataTotal->getReadBytes (); hi->value[mIndex].ll = fDataTotal->getReadBytes (); } else { hist_data->total->value[mIndex].ll = 0; hi->value[mIndex].ll = 0; } break; case BaseMetric::IO_READ_CNT: if (!empty) { hist_data->total->value[mIndex].ll = fDataTotal->getReadCnt (); hi->value[mIndex].ll = fDataTotal->getReadCnt (); } else { hist_data->total->value[mIndex].ll = 0; hi->value[mIndex].ll = 0; } break; case BaseMetric::IO_READ_TIME: if (!empty) { hist_data->total->value[mIndex].d = (double) fDataTotal->getReadTime () / prec; hi->value[mIndex].d = hist_data->total->value[mIndex].d; } else { hist_data->total->value[mIndex].d = 0.0; hi->value[mIndex].d = 0.0; } break; case BaseMetric::IO_WRITE_BYTES: if (!empty) { hist_data->total->value[mIndex].ll = fDataTotal->getWriteBytes (); hi->value[mIndex].ll = fDataTotal->getWriteBytes (); } else { hist_data->total->value[mIndex].ll = 0; hi->value[mIndex].ll = 0; } break; case BaseMetric::IO_WRITE_CNT: if (!empty) { hist_data->total->value[mIndex].ll = fDataTotal->getWriteCnt (); hi->value[mIndex].ll = fDataTotal->getWriteCnt (); } else { hist_data->total->value[mIndex].ll = 0; hi->value[mIndex].ll = 0; } break; case BaseMetric::IO_WRITE_TIME: if (!empty) { hist_data->total->value[mIndex].d = (double) fDataTotal->getWriteTime () / prec; hi->value[mIndex].d = hist_data->total->value[mIndex].d; } else { hist_data->total->value[mIndex].d = 0.0; hi->value[mIndex].d = 0.0; } break; case BaseMetric::IO_OTHER_CNT: if (!empty) { hist_data->total->value[mIndex].ll = fDataTotal->getOtherCnt (); hi->value[mIndex].ll = fDataTotal->getOtherCnt (); } else { hist_data->total->value[mIndex].ll = 0; hi->value[mIndex].ll = 0; } break; case BaseMetric::IO_OTHER_TIME: if (!empty) { hist_data->total->value[mIndex].d = (double) fDataTotal->getOtherTime () / prec; hi->value[mIndex].d = hist_data->total->value[mIndex].d; } else { hist_data->total->value[mIndex].d = 0.0; hi->value[mIndex].d = 0.0; } break; case BaseMetric::IO_ERROR_CNT: if (!empty) { hist_data->total->value[mIndex].ll = fDataTotal->getErrorCnt (); hi->value[mIndex].ll = fDataTotal->getErrorCnt (); } else { hist_data->total->value[mIndex].ll = 0; hi->value[mIndex].ll = 0; } break; case BaseMetric::IO_ERROR_TIME: if (!empty) { hist_data->total->value[mIndex].d = (double) fDataTotal->getErrorTime () / prec; hi->value[mIndex].d = hist_data->total->value[mIndex].d; } else { hist_data->total->value[mIndex].d = 0.0; hi->value[mIndex].d = 0.0; } break; default: break; } } } void IOActivity::computeHistTotals (Hist_data *hist_data, MetricList *mlist) { int mIndex; Metric *mtr; Vec_loop (Metric *, mlist->get_items (), mIndex, mtr) { if (!mtr->is_visible () && !mtr->is_tvisible () && !mtr->is_pvisible ()) continue; Metric::Type mtype = mtr->get_type (); ValueTag vType = mtr->get_vtype (); hist_data->total->value[mIndex].tag = vType; double prec = (double) NANOSEC; switch (mtype) { case BaseMetric::IO_READ_BYTES: hist_data->total->value[mIndex].ll = fDataTotal->getReadBytes (); break; case BaseMetric::IO_READ_CNT: hist_data->total->value[mIndex].ll = fDataTotal->getReadCnt (); break; case BaseMetric::IO_READ_TIME: hist_data->total->value[mIndex].d = (double) fDataTotal->getReadTime () / prec; break; case BaseMetric::IO_WRITE_BYTES: hist_data->total->value[mIndex].ll = fDataTotal->getWriteBytes (); break; case BaseMetric::IO_WRITE_CNT: hist_data->total->value[mIndex].ll = fDataTotal->getWriteCnt (); break; case BaseMetric::IO_WRITE_TIME: hist_data->total->value[mIndex].d = (double) fDataTotal->getWriteTime () / prec; break; case BaseMetric::IO_OTHER_CNT: hist_data->total->value[mIndex].ll = fDataTotal->getOtherCnt (); break; case BaseMetric::IO_OTHER_TIME: hist_data->total->value[mIndex].d = (double) fDataTotal->getOtherTime () / prec; break; case BaseMetric::IO_ERROR_CNT: hist_data->total->value[mIndex].ll = fDataTotal->getErrorCnt (); break; case BaseMetric::IO_ERROR_TIME: hist_data->total->value[mIndex].d = (double) fDataTotal->getErrorTime () / prec; break; default: break; } } } void IOActivity::computeHistData (Hist_data *hist_data, MetricList *mlist, Hist_data::Mode mode, Histable *selObj) { Hist_data::HistItem *hi = NULL; int numObjs = fDataObjs->size (); int numMetrics = mlist->get_items ()->size (); for (int i = 0; i < numObjs; i++) { FileData *fData = fDataObjs->fetch (i); if (mode == Hist_data::ALL) hi = hist_data->append_hist_item (fData); else if (mode == Hist_data::SELF) { if (fData->id == selObj->id) hi = hist_data->append_hist_item (fData); else continue; } for (int mIndex = 0; mIndex < numMetrics; mIndex++) { Metric *mtr = mlist->get_items ()->fetch (mIndex); if (!mtr->is_visible () && !mtr->is_tvisible () && !mtr->is_pvisible ()) continue; Metric::Type mtype = mtr->get_type (); ValueTag vType = mtr->get_vtype (); hi->value[mIndex].tag = vType; double prec = (double) NANOSEC; switch (mtype) { case BaseMetric::IO_READ_BYTES: hi->value[mIndex].ll = fData->getReadBytes (); break; case BaseMetric::IO_READ_CNT: hi->value[mIndex].ll = fData->getReadCnt (); break; case BaseMetric::IO_READ_TIME: hi->value[mIndex].d = (double) fData->getReadTime () / prec; break; case BaseMetric::IO_WRITE_BYTES: hi->value[mIndex].ll = fData->getWriteBytes (); break; case BaseMetric::IO_WRITE_CNT: hi->value[mIndex].ll = fData->getWriteCnt (); break; case BaseMetric::IO_WRITE_TIME: hi->value[mIndex].d = (double) fData->getWriteTime () / prec; break; case BaseMetric::IO_OTHER_CNT: hi->value[mIndex].ll = fData->getOtherCnt (); break; case BaseMetric::IO_OTHER_TIME: hi->value[mIndex].d = (double) fData->getOtherTime () / prec; break; case BaseMetric::IO_ERROR_CNT: hi->value[mIndex].ll = fData->getErrorCnt (); break; case BaseMetric::IO_ERROR_TIME: hi->value[mIndex].d = (double) fData->getErrorTime () / prec; break; default: break; } } } } Hist_data * IOActivity::compute_metrics (MetricList *mlist, Histable::Type type, Hist_data::Mode mode, Histable *selObj) { // it's already there, just return it if (mode == Hist_data::ALL) { if (type == Histable::IOACTFILE && hist_data_file_all) return hist_data_file_all; else if (type == Histable::IOACTVFD && hist_data_vfd_all) return hist_data_vfd_all; else if (type == Histable::IOCALLSTACK && hist_data_callstack_all) return hist_data_callstack_all; } bool has_data = false; Hist_data *hist_data = NULL; VMode viewMode = dbev->get_view_mode (); switch (type) { case Histable::IOACTVFD: if (!hasVfd) computeData (type); // computeData() creates fDataObjsVfd // fDataObjsVfd contains the list of vfd objects if (fDataObjsVfd != NULL) { // fDataObjs is used in other methods fDataObjs = fDataObjsVfd; has_data = true; } else has_data = false; if (has_data && mode == Hist_data::ALL && hist_data_vfd_all == NULL) { hist_data_vfd_all = new Hist_data (mlist, type, mode, true); hist_data = hist_data_vfd_all; } else if (has_data) hist_data = new Hist_data (mlist, type, mode, false); else { hist_data = new Hist_data (mlist, type, mode, false); createHistItemTotals (hist_data, mlist, type, true); return hist_data; } break; case Histable::IOACTFILE: if (!hasFile) computeData (type); // computeData() creates fDataObjsFile // fDataObjsFile contains the list of file objects if (fDataObjsFile != NULL) { fDataObjs = fDataObjsFile; has_data = true; } else has_data = false; if (has_data && mode == Hist_data::ALL && hist_data_file_all == NULL) { hist_data_file_all = new Hist_data (mlist, type, mode, true); hist_data = hist_data_file_all; } else if (has_data) hist_data = new Hist_data (mlist, type, mode, false); else { hist_data = new Hist_data (mlist, type, mode, false); createHistItemTotals (hist_data, mlist, type, true); return hist_data; } break; case Histable::IOCALLSTACK: if (!hasCallStack) computeCallStack (type, viewMode); // computeCallStack() creates fDataObjsCallStack // fDataObjsCallStack contains the list of call stack objects if (fDataObjsCallStack != NULL) { fDataObjs = fDataObjsCallStack; has_data = true; } else has_data = false; if (has_data && (mode == Hist_data::ALL) && (hist_data_callstack_all == NULL)) { hist_data_callstack_all = new Hist_data (mlist, type, mode, true); hist_data = hist_data_callstack_all; } else if (has_data) hist_data = new Hist_data (mlist, type, mode, false); else { hist_data = new Hist_data (mlist, type, mode, false); createHistItemTotals (hist_data, mlist, type, true); return hist_data; } break; default: fprintf (stderr, "IOActivity cannot process data due to wrong Histable (type=%d) \n", type); abort (); } if (mode == Hist_data::ALL || (mode == Hist_data::SELF && selObj->id == 0)) createHistItemTotals (hist_data, mlist, type, false); else computeHistTotals (hist_data, mlist); computeHistData (hist_data, mlist, mode, selObj); // Determine by which metric to sort if any bool rev_sort = mlist->get_sort_rev (); int sort_ind = -1; int nmetrics = mlist->get_items ()->size (); for (int mind = 0; mind < nmetrics; mind++) if (mlist->get_sort_ref_index () == mind) sort_ind = mind; hist_data->sort (sort_ind, rev_sort); hist_data->compute_minmax (); return hist_data; } void IOActivity::computeData (Histable::Type type) { bool has_iodata = false; reset (); int64_t histableId = 0; // It is used by fDataAggr only // fData uses vfd for histable id fDataHash = new HashMap; FileData *fData = NULL; FileData *fDataAggr = NULL; fDataTotal = new FileData (TOTAL_FILENAME); fDataTotal->setHistType (type); fDataTotal->setVirtualFd (VIRTUAL_FD_TOTAL); fDataTotal->id = histableId++; FileData *fDataStdin = new FileData (STDIN_FILENAME); fDataStdin->setFileDes (STDIN_FD); fDataStdin->setHistType (type); fDataStdin->setFsType ("N/A"); fDataStdin->id = histableId++; FileData *fDataStdout = new FileData (STDOUT_FILENAME); fDataStdout->setFileDes (STDOUT_FD); fDataStdout->setHistType (type); fDataStdout->setFsType ("N/A"); fDataStdout->id = histableId++; FileData *fDataStderr = new FileData (STDERR_FILENAME); fDataStderr->setFileDes (STDERR_FD); fDataStderr->setHistType (type); fDataStderr->setFsType ("N/A"); fDataStderr->id = histableId++; FileData *fDataOtherIO = new FileData (OTHERIO_FILENAME); fDataOtherIO->setFileDes (OTHERIO_FD); fDataOtherIO->setHistType (type); fDataOtherIO->setFsType ("N/A"); fDataOtherIO->id = histableId++; DefaultMap* fDataMap; fDataObjsFile = NULL; fDataObjsVfd = NULL; // get the list of io events from DbeView int numExps = dbeSession->nexps (); for (int k = 0; k < numExps; k++) { DataView *ioPkts = dbev->get_filtered_events (k, DATA_IOTRACE); if (ioPkts == NULL || ioPkts->getSize () <= 0) continue; Experiment *exp = dbeSession->get_exp (k); fDataMap = exp->getFDataMap (); if (fDataMap == NULL) continue; delete fDataVfdMap; fDataVfdMap = new DefaultMap; long sz = ioPkts->getSize (); for (long i = 0; i < sz; ++i) { hrtime_t event_duration = ioPkts->getLongValue (PROP_EVT_TIME, i); int64_t nByte = ioPkts->getLongValue (PROP_IONBYTE, i); IOTrace_type ioType = (IOTrace_type) ioPkts->getIntValue (PROP_IOTYPE, i); int64_t vFd = ioPkts->getLongValue (PROP_IOVFD, i); if (vFd >= 0) { fData = fDataMap->get (vFd); if (fData == NULL) continue; } else continue; if (fDataVfdMap->get (vFd) == NULL) fDataVfdMap->put (vFd, fData); switch (ioType) { case READ_TRACE: fData->addReadEvent (event_duration, nByte); // Set the Histable id for IOVFD fData->id = fData->getVirtualFd (); fDataTotal->addReadEvent (event_duration, nByte); fDataTotal->setReadStat (event_duration, nByte); break; case WRITE_TRACE: fData->addWriteEvent (event_duration, nByte); // Set the Histable id for IOVFD fData->id = fData->getVirtualFd (); fDataTotal->addWriteEvent (event_duration, nByte); fDataTotal->setWriteStat (event_duration, nByte); break; case OPEN_TRACE: fData->addOtherEvent (event_duration); // Set the Histable id for IOVFD fData->id = fData->getVirtualFd (); fDataTotal->addOtherEvent (event_duration); break; case CLOSE_TRACE: case OTHERIO_TRACE: fData->addOtherEvent (event_duration); // Set the Histable id for IOVFD fData->id = fData->getVirtualFd (); fDataTotal->addOtherEvent (event_duration); break; case READ_TRACE_ERROR: case WRITE_TRACE_ERROR: case OPEN_TRACE_ERROR: case CLOSE_TRACE_ERROR: case OTHERIO_TRACE_ERROR: fData->addErrorEvent (event_duration); // Set the Histable id for IOVFD fData->id = fData->getVirtualFd (); fDataTotal->addErrorEvent (event_duration); break; case IOTRACETYPE_LAST: break; } if (type == Histable::IOACTFILE) { fDataAggr = fDataHash->get (fData->getFileName ()); if (fDataAggr == NULL) { bool setInfo = false; if (vFd == VIRTUAL_FD_STDIN) fDataAggr = fDataStdin; else if (vFd == VIRTUAL_FD_STDOUT) fDataAggr = fDataStdout; else if (vFd == VIRTUAL_FD_STDERR) fDataAggr = fDataStderr; else if (vFd == VIRTUAL_FD_OTHERIO) fDataAggr = fDataOtherIO; else { fDataAggr = new FileData (fData->getFileName ()); setInfo = true; } fDataHash->put (fData->getFileName (), fDataAggr); if (setInfo) { fDataAggr->setFsType (fData->getFsType ()); fDataAggr->setHistType (type); // Set the Histable id for aggregated file name fDataAggr->id = histableId; fDataAggr->setVirtualFd (histableId); histableId++; } } fDataAggr->setFileDesList (fData->getFileDes ()); fDataAggr->setVirtualFds (fData->getVirtualFd ()); switch (ioType) { case READ_TRACE: fDataAggr->addReadEvent (event_duration, nByte); break; case WRITE_TRACE: fDataAggr->addWriteEvent (event_duration, nByte); break; case OPEN_TRACE: fDataAggr->addOtherEvent (event_duration); break; case CLOSE_TRACE: case OTHERIO_TRACE: fDataAggr->addOtherEvent (event_duration); break; case READ_TRACE_ERROR: case WRITE_TRACE_ERROR: case OPEN_TRACE_ERROR: case CLOSE_TRACE_ERROR: case OTHERIO_TRACE_ERROR: fDataAggr->addErrorEvent (event_duration); break; case IOTRACETYPE_LAST: break; } } has_iodata = true; } if (sz > 0) { if (fDataObjsVfd == NULL) fDataObjsVfd = new Vector; fDataObjsVfd->addAll (fDataVfdMap->values ()); hasVfd = true; } } if (has_iodata && type == Histable::IOACTFILE) { fDataObjsFile = fDataHash->values ()->copy (); hasFile = true; } } void IOActivity::computeCallStack (Histable::Type type, VMode viewMode) { bool has_data = false; int64_t stackIndex = 0; FileData *fData = NULL; delete fDataCalStkMap; fDataCalStkMap = new DefaultMap; delete fDataTotal; fDataTotal = new FileData (TOTAL_FILENAME); fDataTotal->setHistType (type); // There is no call stack for total, use the index for id fDataTotal->id = stackIndex++; // get the list of io events from DbeView int numExps = dbeSession->nexps (); for (int k = 0; k < numExps; k++) { DataView *ioPkts = dbev->get_filtered_events (k, DATA_IOTRACE); if (ioPkts == NULL || ioPkts->getSize () <= 0) continue; long sz = ioPkts->getSize (); for (long i = 0; i < sz; ++i) { hrtime_t event_duration = ioPkts->getLongValue (PROP_EVT_TIME, i); int64_t nByte = ioPkts->getLongValue (PROP_IONBYTE, i); void *stackId = getStack (viewMode, ioPkts, i); IOTrace_type ioType = (IOTrace_type) ioPkts->getIntValue (PROP_IOTYPE, i); int64_t vFd = ioPkts->getLongValue (PROP_IOVFD, i); if (stackId != NULL && vFd > 0) { fData = fDataCalStkMap->get (stackId); if (fData == NULL) { char *stkName = dbe_sprintf (GTXT ("Stack 0x%llx"), (unsigned long long) stackId); fData = new FileData (stkName); fDataCalStkMap->put (stackId, fData); fData->id = (int64_t) stackId; fData->setVirtualFd (stackIndex); stackIndex++; fData->setHistType (type); } } else continue; switch (ioType) { case READ_TRACE: fData->addReadEvent (event_duration, nByte); fDataTotal->addReadEvent (event_duration, nByte); fDataTotal->setReadStat (event_duration, nByte); break; case WRITE_TRACE: fData->addWriteEvent (event_duration, nByte); fDataTotal->addWriteEvent (event_duration, nByte); fDataTotal->setWriteStat (event_duration, nByte); break; case OPEN_TRACE: fData->addOtherEvent (event_duration); fDataTotal->addOtherEvent (event_duration); break; case CLOSE_TRACE: case OTHERIO_TRACE: fData->addOtherEvent (event_duration); fDataTotal->addOtherEvent (event_duration); break; case READ_TRACE_ERROR: case WRITE_TRACE_ERROR: case OPEN_TRACE_ERROR: fData->addErrorEvent (event_duration); fDataTotal->addErrorEvent (event_duration); break; case CLOSE_TRACE_ERROR: case OTHERIO_TRACE_ERROR: fData->addErrorEvent (event_duration); fDataTotal->addErrorEvent (event_duration); break; case IOTRACETYPE_LAST: break; } has_data = true; } } if (has_data) { fDataObjsCallStack = fDataCalStkMap->values ()->copy (); hasCallStack = true; } }