diff --git a/1/Makefile b/1/Makefile index 9115dc1..18172e3 100644 --- a/1/Makefile +++ b/1/Makefile @@ -7,29 +7,56 @@ LDFLAGS := -lrt TARGET := lab1 SRC := main.cpp + LOG_FILE := log.txt -XLSX_FILE:= process_log.xlsx -.PHONY: all run log excel export report deps clean rebuild +MD_FILE := processes.md +IMG_FILE := gantt.png +PY_EXPORT := exporter.py + +.PHONY: all run log export report deps clean rebuild + +# ------------------------- +# BUILD +# ------------------------- all: $(TARGET) $(TARGET): $(SRC) $(CXX) $(CXXFLAGS) $(SRC) -o $(TARGET) $(LDFLAGS) +# ------------------------- +# RUN ONLY +# ------------------------- run: $(TARGET) ./$(TARGET) +# ------------------------- +# LOG ONLY (C++ output) +# ------------------------- log: $(TARGET) ./$(TARGET) > $(LOG_FILE) -excel: log - $(PYTHON) export.py --input $(LOG_FILE) --output $(XLSX_FILE) +# ------------------------- +# EXPORT (C++ + Python pipeline) +# ------------------------- +export: $(TARGET) + ./$(TARGET) > $(LOG_FILE) + $(PYTHON) $(PY_EXPORT) $(LOG_FILE) +# ------------------------- +# DEPENDENCIES +# ------------------------- deps: - $(PIP) install -r req.txt + $(PIP) install matplotlib +# ------------------------- +# CLEAN +# ------------------------- clean: - rm -f $(TARGET) $(XLSX_FILE) + rm -f $(TARGET) $(LOG_FILE) $(MD_FILE) $(IMG_FILE) +# ------------------------- +# REBUILD +# ------------------------- rebuild: clean all diff --git a/1/export.py b/1/export.py deleted file mode 100644 index 5e7cb4a..0000000 --- a/1/export.py +++ /dev/null @@ -1,203 +0,0 @@ -#!/usr/bin/env python3 -import argparse -import re -import sys -from dataclasses import dataclass -from decimal import Decimal -from typing import Dict, Iterable, List, Tuple - -from openpyxl import Workbook -from openpyxl.chart import BarChart, Reference -from openpyxl.styles import Font - - -LINE_RE = re.compile( - r"^(START|END)\s+PID=(\d+)\s+PPID=(\d+)\s+depth=(\d+)\s+range=\[(\d+),(\d+)\]\s+time=([0-9.]+)$" -) - - -@dataclass -class ProcessLog: - pid: int - ppid: int - start: Decimal - start_text: str - finish: Decimal | None = None - finish_text: str | None = None - first_seen_order: int = 0 - - -def parse_args() -> argparse.Namespace: - parser = argparse.ArgumentParser( - description="Convert process START/END log to a ptree-like Excel report." - ) - parser.add_argument( - "-i", - "--input", - default="-", - help="Input log file path (default: stdin)", - ) - parser.add_argument( - "-o", - "--output", - default="process_log.xlsx", - help="Output Excel file path (default: process_log.xlsx)", - ) - return parser.parse_args() - - -def read_lines(input_path: str) -> Iterable[str]: - if input_path == "-": - for line in sys.stdin: - yield line.rstrip("\n") - return - - with open(input_path, "r", encoding="utf-8") as f: - for line in f: - yield line.rstrip("\n") - - -def parse_processes(lines: Iterable[str]) -> Dict[int, ProcessLog]: - processes: Dict[int, ProcessLog] = {} - order = 0 - - for raw in lines: - m = LINE_RE.match(raw.strip()) - if not m: - continue - - event, pid_s, ppid_s, _depth_s, _l_s, _r_s, ts_s = m.groups() - pid = int(pid_s) - ppid = int(ppid_s) - ts = Decimal(ts_s) - - if event == "START": - processes[pid] = ProcessLog( - pid=pid, - ppid=ppid, - start=ts, - start_text=ts_s, - finish=None, - finish_text=None, - first_seen_order=order, - ) - order += 1 - else: - if pid in processes: - processes[pid].finish = ts - processes[pid].finish_text = ts_s - - return processes - - -def tree_rows(processes: Dict[int, ProcessLog]) -> List[Tuple[int, ProcessLog]]: - complete = {pid: p for pid, p in processes.items() if p.finish is not None} - - children: Dict[int, List[int]] = {} - for pid in complete: - children[pid] = [] - for pid, node in complete.items(): - if node.ppid in complete: - children[node.ppid].append(pid) - - for pid in children: - children[pid].sort(key=lambda cpid: complete[cpid].start) - - roots = [pid for pid, node in complete.items() if node.ppid not in complete] - roots.sort(key=lambda pid: complete[pid].start) - - ordered: List[Tuple[int, ProcessLog]] = [] - - def dfs(pid: int, depth: int) -> None: - ordered.append((depth, complete[pid])) - for ch in children[pid]: - dfs(ch, depth + 1) - - for root_pid in roots: - dfs(root_pid, 0) - - remaining = [pid for pid in complete if pid not in {p.pid for _, p in ordered}] - remaining.sort(key=lambda pid: complete[pid].start) - for pid in remaining: - dfs(pid, 0) - - return ordered - - -def fmt6(value: Decimal) -> str: - return f"{value.quantize(Decimal('0.000001'))}" - - -def export_excel(rows: List[Tuple[int, ProcessLog]], output_path: str) -> None: - wb = Workbook() - ws = wb.active - ws.title = "process_logs" - - ws.append(["PPID", "PID", "START", "TIME_FROM_START", "FINISH", "DURATION"]) - for cell in ws[1]: - cell.font = Font(bold=True) - - base_start = min((node.start for _, node in rows), default=Decimal("0")) - - for depth, node in rows: - if node.finish is None: - continue - if node.finish_text is None: - continue - - time_from_start = node.start - base_start - duration = node.finish - node.start - pid_display = f"{' ' * depth}{node.pid}" - ws.append( - [ - node.ppid, - pid_display, - node.start_text, - fmt6(time_from_start), - node.finish_text, - fmt6(duration), - ] - ) - - ws.column_dimensions["A"].width = 12 - ws.column_dimensions["B"].width = 20 - ws.column_dimensions["C"].width = 18 - ws.column_dimensions["D"].width = 18 - ws.column_dimensions["E"].width = 18 - ws.column_dimensions["F"].width = 14 - ws.freeze_panes = "A2" - - last_row = ws.max_row - if last_row >= 2: - chart = BarChart() - chart.type = "bar" - chart.grouping = "stacked" - chart.overlap = 100 - chart.gapWidth = 150 - chart.legend.position = "b" - chart.width = 15 - chart.height = 7.5 - chart.x_axis.scaling.orientation = "maxMin" - - data = Reference(ws, min_col=4, max_col=4, min_row=2, max_row=last_row) - duration = Reference(ws, min_col=6, max_col=6, min_row=2, max_row=last_row) - chart.add_data(data, titles_from_data=False) - chart.add_data(duration, titles_from_data=False) - chart.series[0].graphicalProperties.noFill = True - - ws.add_chart(chart, "D21") - - wb.save(output_path) - - -def main() -> int: - args = parse_args() - processes = parse_processes(read_lines(args.input)) - rows = tree_rows(processes) - export_excel(rows, args.output) - print(f"Saved to {args.output}") - return 0 - - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/1/exporter.py b/1/exporter.py new file mode 100644 index 0000000..ad7df1c --- /dev/null +++ b/1/exporter.py @@ -0,0 +1,97 @@ +import re +import sys + +import matplotlib.pyplot as plt + +LOG_RE = re.compile( + r"(PROC_START|PROC_END)\s+pid=(\d+)\s+ppid=(\d+)\s+depth=(\d+)\s+l=(\d+)\s+r=(\d+)\s+ts=([\d.]+)" +) + + +class Proc: + def __init__(self, pid, ppid): + self.pid = pid + self.ppid = ppid + self.start = None + self.end = None + self.children = [] + + @property + def duration(self): + return self.end - self.start + + +def parse_log(path): + procs = {} + for line in open(path): + m = LOG_RE.search(line) + if not m: + continue + typ, pid, ppid, depth, l, r, ts = m.groups() + pid, ppid, ts = int(pid), int(ppid), float(ts) + if pid not in procs: + procs[pid] = Proc(pid, ppid) + if typ == "PROC_START": + procs[pid].start = ts + else: + procs[pid].end = ts + return procs + + +def build_tree(procs): + root = None + for p in procs.values(): + if p.ppid in procs: + procs[p.ppid].children.append(p) + else: + root = p + return root + + +def dfs_order(root): + order = [] + + def dfs(p): + order.append(p) + for c in sorted(p.children, key=lambda x: x.start): + dfs(c) + + dfs(root) + return order + + +def write_md(procs, ordered): + first = min(p.start for p in procs.values()) + with open("processes.md", "w") as f: + f.write("| PID | PPID | Start | End | Offset | Duration |\n") + f.write("|---|---|---|---|---|---|\n") + for p in ordered: + f.write( + f"| {p.pid} | {p.ppid} | {p.start:.6f} | {p.end:.6f} | {p.start - first:.6f} | {p.duration:.6f} |\n" + ) + + +def draw_gantt(ordered): + first = min(p.start for p in ordered) + fig, ax = plt.subplots(figsize=(12, 6)) + for i, p in enumerate(ordered): + ax.barh(i, p.duration, left=p.start - first, height=0.6) + ax.text(p.start - first, i, f" PID {p.pid}", va="center") + ax.set_yticks(range(len(ordered))) + ax.set_yticklabels([p.pid for p in ordered]) + ax.set_xlabel("Seconds from first process start") + ax.set_title("Process Gantt") + plt.tight_layout() + plt.savefig("gantt.png") + + +def main(): + procs = parse_log(sys.argv[1]) + root = build_tree(procs) + ordered = dfs_order(root) + write_md(procs, ordered) + draw_gantt(ordered) + + +if __name__ == "__main__": + main() diff --git a/1/gantt.png b/1/gantt.png new file mode 100644 index 0000000..e41b0c1 Binary files /dev/null and b/1/gantt.png differ diff --git a/1/lab1 b/1/lab1 index 3d2a863..4b75181 100755 Binary files a/1/lab1 and b/1/lab1 differ diff --git a/1/log.txt b/1/log.txt index a6f4990..c135d8b 100644 --- a/1/log.txt +++ b/1/log.txt @@ -1,36 +1,30 @@ -Before: -58004 89472 63849 21489 55079 3458 75571 99360 41392 19102 39020 191 15438 77248 35268 63821 27080 47931 57519 22445 - -START PID=22020 PPID=22019 depth=0 range=[0,19999] time=1776915463.198584 -START PID=22021 PPID=22020 depth=1 range=[0,9999] time=1776915463.198737 -START PID=22022 PPID=22020 depth=1 range=[10000,19999] time=1776915463.198837 -START PID=22023 PPID=22021 depth=2 range=[0,4999] time=1776915463.198895 -START PID=22024 PPID=22021 depth=2 range=[5000,9999] time=1776915463.198963 -START PID=22025 PPID=22022 depth=2 range=[10000,14999] time=1776915463.199044 -START PID=22026 PPID=22022 depth=2 range=[15000,19999] time=1776915463.199059 -START PID=22027 PPID=22023 depth=3 range=[0,2499] time=1776915463.199078 -START PID=22028 PPID=22024 depth=3 range=[5000,7499] time=1776915463.199143 -START PID=22029 PPID=22023 depth=3 range=[2500,4999] time=1776915463.199144 -START PID=22030 PPID=22024 depth=3 range=[7500,9999] time=1776915463.199226 -START PID=22033 PPID=22025 depth=3 range=[12500,14999] time=1776915463.199313 -START PID=22031 PPID=22025 depth=3 range=[10000,12499] time=1776915463.199356 -START PID=22032 PPID=22026 depth=3 range=[15000,17499] time=1776915463.199389 -END PID=22027 PPID=22023 depth=3 range=[0,2499] time=1776915463.199444 -END PID=22028 PPID=22024 depth=3 range=[5000,7499] time=1776915463.199544 -END PID=22029 PPID=22023 depth=3 range=[2500,4999] time=1776915463.199582 -END PID=22030 PPID=22024 depth=3 range=[7500,9999] time=1776915463.199608 -END PID=22031 PPID=22025 depth=3 range=[10000,12499] time=1776915463.199723 -END PID=22033 PPID=22025 depth=3 range=[12500,14999] time=1776915463.199746 -END PID=22023 PPID=22021 depth=2 range=[0,4999] time=1776915463.199774 -END PID=22024 PPID=22021 depth=2 range=[5000,9999] time=1776915463.199782 -START PID=22034 PPID=22026 depth=3 range=[17500,19999] time=1776915463.199843 -END PID=22025 PPID=22022 depth=2 range=[10000,14999] time=1776915463.199932 -END PID=22021 PPID=22020 depth=1 range=[0,9999] time=1776915463.199996 -END PID=22034 PPID=22026 depth=3 range=[17500,19999] time=1776915463.200103 -END PID=22032 PPID=22026 depth=3 range=[15000,17499] time=1776915463.200222 -END PID=22026 PPID=22022 depth=2 range=[15000,19999] time=1776915463.200412 -END PID=22022 PPID=22020 depth=1 range=[10000,19999] time=1776915463.200655 -END PID=22020 PPID=22019 depth=0 range=[0,19999] time=1776915463.200993 - -After: -10 11 20 40 40 43 51 56 58 72 74 80 96 96 98 103 108 113 119 137 +PROC_START pid=41115 ppid=41113 depth=0 l=0 r=19999 ts=1777074730.331155 +PROC_START pid=41116 ppid=41115 depth=1 l=0 r=9999 ts=1777074730.332433 +PROC_START pid=41117 ppid=41115 depth=1 l=10000 r=19999 ts=1777074730.332633 +PROC_START pid=41118 ppid=41116 depth=2 l=0 r=4999 ts=1777074730.333202 +PROC_START pid=41119 ppid=41117 depth=2 l=10000 r=14999 ts=1777074730.333253 +PROC_START pid=41120 ppid=41116 depth=2 l=5000 r=9999 ts=1777074730.333499 +PROC_START pid=41121 ppid=41117 depth=2 l=15000 r=19999 ts=1777074730.333522 +PROC_START pid=41122 ppid=41118 depth=3 l=0 r=2499 ts=1777074730.333905 +PROC_START pid=41123 ppid=41119 depth=3 l=10000 r=12499 ts=1777074730.334001 +PROC_START pid=41124 ppid=41121 depth=3 l=15000 r=17499 ts=1777074730.334220 +PROC_START pid=41125 ppid=41120 depth=3 l=5000 r=7499 ts=1777074730.334244 +PROC_START pid=41127 ppid=41119 depth=3 l=12500 r=14999 ts=1777074730.334636 +PROC_START pid=41126 ppid=41118 depth=3 l=2500 r=4999 ts=1777074730.334760 +PROC_START pid=41128 ppid=41121 depth=3 l=17500 r=19999 ts=1777074730.334799 +PROC_START pid=41129 ppid=41120 depth=3 l=7500 r=9999 ts=1777074730.334870 +PROC_END pid=41123 ppid=41119 depth=3 l=10000 r=12499 ts=1777074730.336351 +PROC_END pid=41127 ppid=41119 depth=3 l=12500 r=14999 ts=1777074730.336705 +PROC_END pid=41124 ppid=41121 depth=3 l=15000 r=17499 ts=1777074730.336857 +PROC_END pid=41129 ppid=41120 depth=3 l=7500 r=9999 ts=1777074730.336864 +PROC_END pid=41125 ppid=41120 depth=3 l=5000 r=7499 ts=1777074730.337630 +PROC_END pid=41119 ppid=41117 depth=2 l=10000 r=14999 ts=1777074730.337559 +PROC_END pid=41128 ppid=41121 depth=3 l=17500 r=19999 ts=1777074730.337974 +PROC_END pid=41121 ppid=41117 depth=2 l=15000 r=19999 ts=1777074730.338710 +PROC_END pid=41120 ppid=41116 depth=2 l=5000 r=9999 ts=1777074730.338724 +PROC_END pid=41117 ppid=41115 depth=1 l=10000 r=19999 ts=1777074730.339777 +PROC_END pid=41122 ppid=41118 depth=3 l=0 r=2499 ts=1777074730.340717 +PROC_END pid=41126 ppid=41118 depth=3 l=2500 r=4999 ts=1777074730.340948 +PROC_END pid=41118 ppid=41116 depth=2 l=0 r=4999 ts=1777074730.341717 +PROC_END pid=41116 ppid=41115 depth=1 l=0 r=9999 ts=1777074730.342666 +PROC_END pid=41115 ppid=41113 depth=0 l=0 r=19999 ts=1777074730.343971 diff --git a/1/main.cpp b/1/main.cpp index 9312bd4..1e9d0f4 100644 --- a/1/main.cpp +++ b/1/main.cpp @@ -28,17 +28,33 @@ std::string now() { return oss.str(); } +/* ========= ONLY MODIFIED PART ========= */ void log_start(int l, int r, int depth) { - std::cout << "START PID=" << getpid() << " PPID=" << getppid() << " depth=" << depth - << " range=[" << l << "," << r << "] time=" << now() << '\n' - << std::flush; + std::cout + << "PROC_START" + << " pid=" << getpid() + << " ppid=" << getppid() + << " depth=" << depth + << " l=" << l + << " r=" << r + << " ts=" << now() + << '\n' + << std::flush; } void log_end(int l, int r, int depth) { - std::cout << "END PID=" << getpid() << " PPID=" << getppid() << " depth=" << depth - << " range=[" << l << "," << r << "] time=" << now() << '\n' - << std::flush; + std::cout + << "PROC_END" + << " pid=" << getpid() + << " ppid=" << getppid() + << " depth=" << depth + << " l=" << l + << " r=" << r + << " ts=" << now() + << '\n' + << std::flush; } +/* ====================================== */ void merge_range(int* arr, int l, int m, int r) { std::vector temp; @@ -55,20 +71,14 @@ void merge_range(int* arr, int l, int m, int r) { } } - while (i <= m) { - temp.push_back(arr[i++]); - } - while (j <= r) { - temp.push_back(arr[j++]); - } + while (i <= m) temp.push_back(arr[i++]); + while (j <= r) temp.push_back(arr[j++]); std::copy(temp.begin(), temp.end(), arr + l); } void local_sort(int* arr, int l, int r) { - if (l >= r) { - return; - } + if (l >= r) return; const int m = l + (r - l) / 2; local_sort(arr, l, m); @@ -137,19 +147,11 @@ bool parse_positive_int(const char* value, int& out) { try { size_t consumed = 0; const int parsed = std::stoi(value, &consumed); - if (value[consumed] != '\0') { - return false; - } - if (parsed <= 0) { - return false; - } + if (value[consumed] != '\0') return false; + if (parsed <= 0) return false; out = parsed; return true; - } catch (const std::invalid_argument&) { - return false; - } catch (const std::out_of_range&) { - return false; - } + } catch (...) { return false; } } } // namespace @@ -183,30 +185,12 @@ int main(int argc, char* argv[]) { std::mt19937 rng(std::random_device{}()); std::uniform_int_distribution dist(0, 99999); - for (int i = 0; i < n; ++i) { - arr[i] = dist(rng); - } - - std::cout << "Before:\n"; - for (int i = 0; i < std::min(n, kPreviewCount); ++i) { - std::cout << arr[i] << ' '; - } - std::cout << "\n\n"; + for (int i = 0; i < n; ++i) arr[i] = dist(rng); parallel_sort(arr, 0, n - 1, 0, max_depth); - std::cout << "\nAfter:\n"; - for (int i = 0; i < std::min(n, kPreviewCount); ++i) { - std::cout << arr[i] << ' '; - } - std::cout << '\n'; - - if (shmdt(arr) < 0) { - std::perror("shmdt"); - } - if (shmctl(shmid, IPC_RMID, nullptr) < 0) { - std::perror("shmctl IPC_RMID"); - } + if (shmdt(arr) < 0) std::perror("shmdt"); + if (shmctl(shmid, IPC_RMID, nullptr) < 0) std::perror("shmctl IPC_RMID"); return EXIT_SUCCESS; } diff --git a/1/processes.md b/1/processes.md new file mode 100644 index 0000000..2fdfdcc --- /dev/null +++ b/1/processes.md @@ -0,0 +1,17 @@ +| PID | PPID | Start | End | Offset | Duration | +|---|---|---|---|---|---| +| 41115 | 41113 | 1777074730.331155 | 1777074730.343971 | 0.000000 | 0.012816 | +| 41116 | 41115 | 1777074730.332433 | 1777074730.342666 | 0.001278 | 0.010233 | +| 41118 | 41116 | 1777074730.333202 | 1777074730.341717 | 0.002047 | 0.008515 | +| 41122 | 41118 | 1777074730.333905 | 1777074730.340717 | 0.002750 | 0.006812 | +| 41126 | 41118 | 1777074730.334760 | 1777074730.340948 | 0.003605 | 0.006188 | +| 41120 | 41116 | 1777074730.333499 | 1777074730.338724 | 0.002344 | 0.005225 | +| 41125 | 41120 | 1777074730.334244 | 1777074730.337630 | 0.003089 | 0.003386 | +| 41129 | 41120 | 1777074730.334870 | 1777074730.336864 | 0.003715 | 0.001994 | +| 41117 | 41115 | 1777074730.332633 | 1777074730.339777 | 0.001478 | 0.007144 | +| 41119 | 41117 | 1777074730.333253 | 1777074730.337559 | 0.002098 | 0.004306 | +| 41123 | 41119 | 1777074730.334001 | 1777074730.336351 | 0.002846 | 0.002350 | +| 41127 | 41119 | 1777074730.334636 | 1777074730.336705 | 0.003481 | 0.002069 | +| 41121 | 41117 | 1777074730.333522 | 1777074730.338710 | 0.002367 | 0.005188 | +| 41124 | 41121 | 1777074730.334220 | 1777074730.336857 | 0.003065 | 0.002637 | +| 41128 | 41121 | 1777074730.334799 | 1777074730.337974 | 0.003644 | 0.003175 |