diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7c12220 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.xlsx diff --git a/Makefile b/Makefile index 514e865..64e7164 100644 --- a/Makefile +++ b/Makefile @@ -1,19 +1,16 @@ -# Компилятор -CXX = g++ +CXX := g++ +PYTHON := python3 +PIP := $(PYTHON) -m pip -# Флаги -CXXFLAGS = -Wall -Wextra -O2 -std=c++17 +CXXFLAGS := -Wall -Wextra -O2 -std=c++17 +LDFLAGS := -lrt -# Линковка (для shm, fork, etc.) -LDFLAGS = -lrt +TARGET := lab1 +SRC := main.cpp +LOG_FILE := log.txt +XLSX_FILE:= process_log.xlsx -# Имя программы -TARGET = lab1 - -# Исходник -SRC = main.cpp - -# ========================== +.PHONY: all run log excel export report deps clean rebuild all: $(TARGET) @@ -23,7 +20,18 @@ $(TARGET): $(SRC) run: $(TARGET) ./$(TARGET) +log: $(TARGET) + ./$(TARGET) > $(LOG_FILE) + +excel export: log + $(PYTHON) export.py --input $(LOG_FILE) --output $(XLSX_FILE) + +report: excel + +deps: + $(PIP) install -r req.txt + clean: - rm -f $(TARGET) + rm -f $(TARGET) $(XLSX_FILE) rebuild: clean all diff --git a/export.py b/export.py new file mode 100644 index 0000000..27d6e53 --- /dev/null +++ b/export.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +import argparse +import re +import sys +from dataclasses import dataclass +from typing import Dict, Iterable, List, Tuple + +from openpyxl import Workbook +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: float + finish: float | 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 = float(ts_s) + + if event == "START": + processes[pid] = ProcessLog( + pid=pid, ppid=ppid, start=ts, finish=None, first_seen_order=order + ) + order += 1 + else: + if pid in processes: + processes[pid].finish = ts + + 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 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", "FINISH", "DURATION"]) + for cell in ws[1]: + cell.font = Font(bold=True) + + for depth, node in rows: + if node.finish is None: + continue + duration = node.finish - node.start + pid_display = f"{' ' * depth}{node.pid}" + ws.append([node.ppid, pid_display, node.start, node.finish, 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 = 14 + ws.freeze_panes = "A2" + + 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/lab1 b/lab1 index b5f66d8..e568c81 100755 Binary files a/lab1 and b/lab1 differ diff --git a/log.txt b/log.txt new file mode 100644 index 0000000..913ac00 --- /dev/null +++ b/log.txt @@ -0,0 +1,36 @@ +Before: +61348 28446 8318 49147 24393 15112 24969 66655 3374 5666 48922 778 11600 6589 6406 56105 71867 17651 21741 7869 + +START PID=44192 PPID=44191 depth=0 range=[0,19999] time=1776745157.209104 +START PID=44193 PPID=44192 depth=1 range=[0,9999] time=1776745157.209289 +START PID=44194 PPID=44192 depth=1 range=[10000,19999] time=1776745157.209369 +START PID=44195 PPID=44193 depth=2 range=[0,4999] time=1776745157.209470 +START PID=44197 PPID=44193 depth=2 range=[5000,9999] time=1776745157.209540 +START PID=44196 PPID=44194 depth=2 range=[10000,14999] time=1776745157.209546 +START PID=44198 PPID=44194 depth=2 range=[15000,19999] time=1776745157.209581 +START PID=44199 PPID=44195 depth=3 range=[0,2499] time=1776745157.209631 +START PID=44200 PPID=44197 depth=3 range=[5000,7499] time=1776745157.209689 +START PID=44201 PPID=44196 depth=3 range=[10000,12499] time=1776745157.209717 +START PID=44202 PPID=44195 depth=3 range=[2500,4999] time=1776745157.209722 +START PID=44204 PPID=44197 depth=3 range=[7500,9999] time=1776745157.209779 +START PID=44205 PPID=44196 depth=3 range=[12500,14999] time=1776745157.209848 +START PID=44203 PPID=44198 depth=3 range=[15000,17499] time=1776745157.209874 +END PID=44200 PPID=44197 depth=3 range=[5000,7499] time=1776745157.209961 +END PID=44199 PPID=44195 depth=3 range=[0,2499] time=1776745157.210015 +END PID=44201 PPID=44196 depth=3 range=[10000,12499] time=1776745157.210100 +END PID=44202 PPID=44195 depth=3 range=[2500,4999] time=1776745157.210110 +END PID=44204 PPID=44197 depth=3 range=[7500,9999] time=1776745157.210143 +END PID=44205 PPID=44196 depth=3 range=[12500,14999] time=1776745157.210214 +END PID=44203 PPID=44198 depth=3 range=[15000,17499] time=1776745157.210242 +END PID=44195 PPID=44193 depth=2 range=[0,4999] time=1776745157.210294 +END PID=44197 PPID=44193 depth=2 range=[5000,9999] time=1776745157.210304 +END PID=44196 PPID=44194 depth=2 range=[10000,14999] time=1776745157.210415 +START PID=44206 PPID=44198 depth=3 range=[17500,19999] time=1776745157.210418 +END PID=44193 PPID=44192 depth=1 range=[0,9999] time=1776745157.210535 +END PID=44206 PPID=44198 depth=3 range=[17500,19999] time=1776745157.210718 +END PID=44198 PPID=44194 depth=2 range=[15000,19999] time=1776745157.210864 +END PID=44194 PPID=44192 depth=1 range=[10000,19999] time=1776745157.211079 +END PID=44192 PPID=44191 depth=0 range=[0,19999] time=1776745157.211362 + +After: +4 10 13 28 36 40 54 55 59 62 68 78 81 81 83 85 90 91 91 92 diff --git a/main.cpp b/main.cpp index 3d35770..9312bd4 100644 --- a/main.cpp +++ b/main.cpp @@ -1,152 +1,212 @@ +#include +#include +#include #include +#include +#include +#include +#include #include #include +#include #include #include -#include -#include -#include -#include -#include +#include -using namespace std; +namespace { -#define MAX 100000 -#define MAX_DEPTH 3 +constexpr int kDefaultN = 20000; +constexpr int kDefaultMaxDepth = 3; +constexpr int kPreviewCount = 20; -struct SharedData { - int arr[MAX]; -}; - -string now() { - timeval tv; - gettimeofday(&tv, NULL); - - ostringstream oss; - oss << tv.tv_sec << "." - << setfill('0') << setw(6) << tv.tv_usec; +std::string now() { + timeval tv{}; + gettimeofday(&tv, nullptr); + std::ostringstream oss; + oss << tv.tv_sec << "." << std::setfill('0') << std::setw(6) << tv.tv_usec; return oss.str(); } -void merge(int *arr, int l, int m, int r) { - int *temp = new int[r - l + 1]; +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; +} - int i = l, j = m + 1, k = 0; +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; +} + +void merge_range(int* arr, int l, int m, int r) { + std::vector temp; + temp.reserve(r - l + 1); + + int i = l; + int j = m + 1; while (i <= m && j <= r) { - if (arr[i] < arr[j]) temp[k++] = arr[i++]; - else temp[k++] = arr[j++]; + if (arr[i] <= arr[j]) { + temp.push_back(arr[i++]); + } else { + temp.push_back(arr[j++]); + } } - while (i <= m) temp[k++] = arr[i++]; - while (j <= r) temp[k++] = arr[j++]; + while (i <= m) { + temp.push_back(arr[i++]); + } + while (j <= r) { + temp.push_back(arr[j++]); + } - for (int x = 0; x < k; x++) arr[l + x] = temp[x]; - - delete[] temp; + std::copy(temp.begin(), temp.end(), arr + l); } -void local_sort(int *arr, int l, int r) { - if (l >= r) return; - int m = (l + r) / 2; - local_sort(arr, l, m); - local_sort(arr, m + 1, r); - merge(arr, l, m, r); -} - -void parallel_sort(int l, int r, int depth, int shmid) { - SharedData *data = (SharedData*) shmat(shmid, NULL, 0); - int *arr = data->arr; - - string start_time = now(); - - cout << "START PID=" << getpid() - << " PPID=" << getppid() - << " depth=" << depth - << " range=[" << l << "," << r << "]" - << " time=" << start_time << "\n"; - +void local_sort(int* arr, int l, int r) { if (l >= r) { - string end_time = now(); - cout << "END PID=" << getpid() - << " range=[" << l << "," << r << "]" - << " time=" << end_time << "\n"; - shmdt(data); return; } - int m = (l + r) / 2; + const int m = l + (r - l) / 2; + local_sort(arr, l, m); + local_sort(arr, m + 1, r); + merge_range(arr, l, m, r); +} - if (depth >= MAX_DEPTH) { +void parallel_sort(int* arr, int l, int r, int depth, int max_depth) { + log_start(l, r, depth); + + if (l >= r) { + log_end(l, r, depth); + return; + } + + const int m = l + (r - l) / 2; + if (depth >= max_depth) { local_sort(arr, l, r); - - string end_time = now(); - cout << "END PID=" << getpid() - << " depth=" << depth - << " range=[" << l << "," << r << "]" - << " time=" << end_time << "\n"; - - shmdt(data); + log_end(l, r, depth); return; } pid_t left = fork(); - if (left == 0) { - parallel_sort(l, m, depth + 1, shmid); - _exit(0); + parallel_sort(arr, l, m, depth + 1, max_depth); + _exit(EXIT_SUCCESS); + } + + if (left < 0) { + std::perror("fork left"); + local_sort(arr, l, m); } pid_t right = fork(); - if (right == 0) { - parallel_sort(m + 1, r, depth + 1, shmid); - _exit(0); + parallel_sort(arr, m + 1, r, depth + 1, max_depth); + _exit(EXIT_SUCCESS); } - waitpid(left, NULL, 0); - waitpid(right, NULL, 0); + if (right < 0) { + std::perror("fork right"); + local_sort(arr, m + 1, r); + } - merge(arr, l, m, r); + int left_status = 0; + if (left > 0 && waitpid(left, &left_status, 0) < 0) { + std::perror("waitpid left"); + local_sort(arr, l, m); + } else if (left > 0 && (!WIFEXITED(left_status) || WEXITSTATUS(left_status) != 0)) { + local_sort(arr, l, m); + } - string end_time = now(); + int right_status = 0; + if (right > 0 && waitpid(right, &right_status, 0) < 0) { + std::perror("waitpid right"); + local_sort(arr, m + 1, r); + } else if (right > 0 && (!WIFEXITED(right_status) || WEXITSTATUS(right_status) != 0)) { + local_sort(arr, m + 1, r); + } - cout << "END PID=" << getpid() - << " depth=" << depth - << " range=[" << l << "," << r << "]" - << " time=" << end_time << "\n"; - - shmdt(data); + merge_range(arr, l, m, r); + log_end(l, r, depth); } -int main() { - key_t key = ftok("shmfile", 65); - int shmid = shmget(key, sizeof(SharedData), 0666 | IPC_CREAT); - - SharedData *data = (SharedData*) shmat(shmid, NULL, 0); - - int n = 20000; - - srand(time(NULL)); - - for (int i = 0; i < n; i++) - data->arr[i] = rand() % 100000; - - cout << "Before:\n"; - for (int i = 0; i < 20; i++) - cout << data->arr[i] << " "; - cout << "\n\n"; - - parallel_sort(0, n - 1, 0, shmid); - - cout << "\nAfter:\n"; - for (int i = 0; i < 20; i++) - cout << data->arr[i] << " "; - cout << "\n"; - - shmdt(data); - shmctl(shmid, IPC_RMID, NULL); - - return 0; +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; + } + out = parsed; + return true; + } catch (const std::invalid_argument&) { + return false; + } catch (const std::out_of_range&) { + return false; + } +} + +} // namespace + +int main(int argc, char* argv[]) { + int n = kDefaultN; + int max_depth = kDefaultMaxDepth; + + if (argc >= 2 && !parse_positive_int(argv[1], n)) { + std::cerr << "Invalid array size: " << argv[1] << '\n'; + return EXIT_FAILURE; + } + if (argc >= 3 && !parse_positive_int(argv[2], max_depth)) { + std::cerr << "Invalid max depth: " << argv[2] << '\n'; + return EXIT_FAILURE; + } + + const size_t shm_size = static_cast(n) * sizeof(int); + const int shmid = shmget(IPC_PRIVATE, shm_size, IPC_CREAT | 0600); + if (shmid < 0) { + std::perror("shmget"); + return EXIT_FAILURE; + } + + int* arr = static_cast(shmat(shmid, nullptr, 0)); + if (arr == reinterpret_cast(-1)) { + std::perror("shmat"); + shmctl(shmid, IPC_RMID, nullptr); + return EXIT_FAILURE; + } + + 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"; + + 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"); + } + + return EXIT_SUCCESS; } diff --git a/process_log.xlsx b/process_log.xlsx new file mode 100644 index 0000000..434c964 Binary files /dev/null and b/process_log.xlsx differ diff --git a/req.txt b/req.txt new file mode 100644 index 0000000..794cc3d --- /dev/null +++ b/req.txt @@ -0,0 +1 @@ +openpyxl