-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
152 lines (121 loc) · 4.38 KB
/
app.py
File metadata and controls
152 lines (121 loc) · 4.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
#!/usr/bin/env python3
"""
Guinea pig program for debugging tutorial: pdb, strace, gdb.
Features:
- TCP server on port 8080 (HTTP responses)
- Periodic log file writes (app.log)
- CPU-bound fibonacci work on a timer
- Intentional bug: division by zero on /crash endpoint
- Threading: main thread (server) + worker thread (background tasks)
"""
import http.server
import json
import logging
import os
import socketserver
import threading
import time
# --- Logging setup ---
LOG_FILE = os.path.join(os.path.dirname(os.path.abspath(__file__)), "app.log")
file_handler = logging.FileHandler(LOG_FILE)
file_handler.setLevel(logging.INFO)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.WARNING)
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s [%(threadName)s] %(levelname)s %(message)s",
handlers=[file_handler, console_handler],
)
logger = logging.getLogger("app")
# --- CPU work ---
def fibonacci(n):
"""Intentionally naive recursive fibonacci for CPU burn."""
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)
# --- Background worker thread ---
def background_worker():
"""Runs periodic tasks: log heartbeat + fibonacci calculation."""
tick = 0
while True:
tick += 1
logger.info(f"worker tick={tick}")
# CPU burn: compute fib(30) every cycle
result = fibonacci(30)
logger.info(f"fibonacci(30) = {result}")
time.sleep(5)
# --- Intentional bug helper ---
def compute_ratio(a, b):
"""
BUG: When b equals a, we get division by zero.
The denominator should probably handle this case.
"""
return a / (b - a)
# --- HTTP request handler ---
class DebugHandler(http.server.BaseHTTPRequestHandler):
request_count = 0
def log_message(self, format, *args):
logger.info(f"HTTP {format % args}")
def do_GET(self):
DebugHandler.request_count += 1
count = DebugHandler.request_count
if self.path == "/":
self._respond(200, {
"message": "Hello from the debugging guinea pig!",
"request_number": count,
"pid": os.getpid(),
"threads": threading.active_count(),
})
elif self.path == "/fib":
n = 35
logger.info(f"Computing fibonacci({n}) on request thread...")
result = fibonacci(n)
self._respond(200, {"fibonacci": n, "result": result})
elif self.path == "/crash":
# BUG: This will divide by zero when called with matching values.
# Try: curl http://localhost:8080/crash
logger.info("Triggering intentional bug...")
value = 42
try:
result = compute_ratio(value, value) # 42 / (42-42) -> ZeroDivisionError
except ZeroDivisionError:
import pdb; pdb.post_mortem()
self._respond(200, {"result": result})
elif self.path == "/info":
self._respond(200, {
"pid": os.getpid(),
"threads": threading.active_count(),
"thread_names": [t.name for t in threading.enumerate()],
"request_count": count,
"log_file": LOG_FILE,
})
else:
self._respond(404, {"error": "not found"})
def _respond(self, status, data):
body = json.dumps(data, indent=2).encode()
self.send_response(status)
self.send_header("Content-Type", "application/json")
self.send_header("Content-Length", str(len(body)))
self.end_headers()
self.wfile.write(body)
# --- Main ---
class ThreadedTCPServer(socketserver.ThreadingMixIn, http.server.HTTPServer):
allow_reuse_address = True
daemon_threads = True
def main():
port = int(os.environ.get("PORT", 8080))
# Start background worker thread
worker = threading.Thread(target=background_worker, name="BackgroundWorker", daemon=True)
worker.start()
logger.info(f"Background worker started (thread={worker.name})")
# Start HTTP server
server = ThreadedTCPServer(("0.0.0.0", port), DebugHandler)
logger.info(f"Serving on port {port} (pid={os.getpid()})")
logger.info("Endpoints: / /fib /crash /info")
try:
server.serve_forever()
except KeyboardInterrupt:
logger.info("Shutting down...")
server.shutdown()
if __name__ == "__main__":
main()