Skip to content

Timeline (TimeGraph) feature - #25

Open
hiba1204 wants to merge 5 commits into
eclipse-tmll:mainfrom
hiba1204:timeline-feature
Open

Timeline (TimeGraph) feature#25
hiba1204 wants to merge 5 commits into
eclipse-tmll:mainfrom
hiba1204:timeline-feature

Conversation

@hiba1204

@hiba1204 hiba1204 commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

What it does

Implements timeline (TimeGraph) support in TMLL (states, arrows, CLI, MCP tools):

  • TimeGraphState now carries style, value, tags (types verified against the official TSP OpenAPI spec), in addition to existing start/end/label
  • New TimeGraphArrow model (source_id, target_id, start, end, duration, style); TimeGraph now carries an arrows list, populated separately via TimeGraph.parse_tsp_arrows since arrows come from a different TSP endpoint than states
  • New CLI timeline command: fetches TIME_GRAPH outputs via experiment.find_outputs(type=['time_graph']) and TMLLClient.fetch_data, with -k/--keywords, -o/--output, --start/--end, --entries, and -p/--plot (renders a Gantt-style PNG)
  • Arrows are fetched via fetch_timegraph_arrows and included in the timeline output as {states: [...], arrows: [...]}
  • New MCP tools: fetch_timeline (wraps the CLI command) and plot_timeline (renders and returns a Gantt-style PNG as an MCP Image)

How to test

Prerequisites:

  • run from the repo root (cd tmll), with the test-trace-files submodule initialized (git submodule update --init) and a local trace-compass-server running.
  1. Start a local trace-compass-server and create an experiment from a test trace, e.g.:
python3 -m tmll.mcp.cli create "$(pwd)/tests/test-trace-files/ctf/src/main/resources/kernel" -n "Test Timeline"
  1. Fetch timeline data:
python3 -m tmll.mcp.cli timeline <experiment_uuid> -k "Resources Status" --entries 1
  1. Render a Gantt chart (use --entries to keep the image small/fast, omitting it renders every entry, which can be slow and hard to read for outputs with many rows):
   python3 -m tmll.mcp.cli timeline <experiment_uuid> -k "Thread Status" --entries 1 2 3 -p /tmp/gantt.png
  1. Both fetch_timeline and plot_timeline were also tested via MCP Inspector:
npx @modelcontextprotocol/inspector python3 /path/to/tmll/tmll/mcp/server.py /path/to/tmll/tmll/mcp/cli.py

Follow-ups

  • fetch_data's default resample_freq (1s) means time windows narrower than ~1s return zero states, even if raw states exist in that window, worth keeping in mind when choosing --start/--end.
  • -p/--plot only renders the first output matching the given keywords if multiple outputs match.
  • Future follow-up: add an interactive HTML rendering option to plot_timeline (similar to plot_xy_with_anomalies's Plotly-based interactive mode), for richer exploration than the static PNG.
  • Future follow-up: a timeline-specific ML module (tmll/ml/modules/timeline/timeline_analysis_module.py), covering:
    • State duration anomalies: detect entries with abnormally long/short states
    • Scheduling analysis: blocked/waiting time analysis
    • Critical path detection: use arrows to find scheduling chains
    • State frequency analysis: histograms of state durations per label
  • Unrelated: my editor's format-on-save reformatted some pre-existing long lines in cli.py and server.py (no logic changes, verified with git diff -w). Happy to revert those specific formatting changes if a tighter diff is preferred.

AI disclosure: Portions of this contribution (code and PR description) were created with the assistance of Claude Sonnet 5 (Anthropic). All AI-assisted code and output were reviewed and verified by me, including testing against a live trace-server, before submission.

Review checklist

  • As an author, I have thoroughly tested my changes and carefully followed the instructions in this template

…hArrow model

- TimeGraphState now carries style, value, tags (types verified against
  the official TSP OpenAPI spec)
- New TimeGraphArrow model (source_id, target_id, start, end, duration, style)
- TimeGraph now carries an arrows list, populated separately via
  TimeGraph.parse_tsp_arrows since arrows come from a different TSP
  endpoint than states
- New 'timeline' subcommand: fetches TIME_GRAPH outputs via
  experiment.find_outputs(type=['time_graph']) and TMLLClient.fetch_data
- Supports -k/--keywords, -o/--output, --start/--end, --entries
- Fetches arrows via fetch_timegraph_arrows and includes them in the
  output as {states: [...], arrows: [...]}
- Adds -p/--plot to render a Gantt-style PNG
- fetch_timeline: wraps the CLI 'timeline' command, returns JSON with
  states and arrows for TIME_GRAPH outputs
- plot_timeline: renders a Gantt-style PNG (reuses states_to_bars,
  group_states_by_entry, get_label_colors from the CLI drawing logic),
  returned as an MCP Image

@kavehshahedi kavehshahedi left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the contribution, Hiba! It looks very good to me!
Just a minor issue about code duplication, everything else is fine :)

Comment thread tmll/mcp/cli.py Outdated
Comment on lines +96 to +164
COLOR_PALETTE = ["green", "red", "blue", "orange", "purple", "gray"]
BAR_HEIGHT = 9


def states_to_bars(states):
return [(state["start_time"], state["end_time"] - state["start_time"]) for state in states]


def group_states_by_entry(states):
grouped = {}
for state in states:
entry = state["entry_name"]
if entry not in grouped:
grouped[entry] = []
grouped[entry].append(state)
return grouped


def get_label_colors(states):
label_colors = {}
for state in states:
label = state["label"]
if label is None or (isinstance(label, float) and label != label):
label = "(no label)"
if label not in label_colors:
label_colors[label] = COLOR_PALETTE[len(
label_colors) % len(COLOR_PALETTE)]
return label_colors


def draw_gantt_to_file(states, output_path):
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import matplotlib.patches as mpatches

label_colors = get_label_colors(states)
grouped = group_states_by_entry(states)

fig, ax = plt.subplots(figsize=(12, max(4, len(grouped) * 0.5)))
ax.set_title("TMLL Timeline — Gantt Chart")
ax.set_xlabel("Time (ns)")

yticks = []
ylabels = []
for i, (entry_name, entry_states) in enumerate(grouped.items()):
bars = states_to_bars(entry_states)
colors = []
for state in entry_states:
label = state["label"]
if label is None or (isinstance(label, float) and label != label):
label = "(no label)"
colors.append(label_colors[label])
y_pos = i * 10
ax.broken_barh(bars, (y_pos, BAR_HEIGHT), facecolors=colors)
yticks.append(y_pos + BAR_HEIGHT / 2)
ylabels.append(entry_name)

ax.set_yticks(yticks)
ax.set_yticklabels(ylabels)
ax.set_ylim(-1, len(grouped) * 10)

handles = [mpatches.Patch(color=color, label=label)
for label, color in label_colors.items()]
ax.legend(handles=handles, loc="upper right")

fig.tight_layout()
plt.savefig(output_path, bbox_inches="tight")
plt.close(fig)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel that this code block has some duplications/redundancies with server.py. If their purpose is almost the same (or very similar), you can reuse them (or tweak a bit to be reusable).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, thanks! I extracted the shared logic into tmll/mcp/gantt.py. Now both cli.py and server.py call build_gantt_figure() from there.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great! Thanks for refactoring the code.
Just one tiny minor suggestion: could you put gantt.py into a proper sub-directory to improve repo's structure? Maube inside tmll/mcp/plots/ or tmll/mcp/ui/? You decide which is more appropriate.

… Addresses review feedback from @kavehshahedi: states_to_bars, group_states_by_entry, get_label_colors, and the figure-building logic were duplicated between cli.py and server.py. Both now import build_gantt_figure() from the new shared module.
fetch_data's TIME_GRAPH case previously only extracted start_time/
end_time/label from each state, dropping style and value even though
TimeGraphState parses them from the raw TSP response. Adds both fields
to the DataFrame built in fetch_data.

Fixes eclipse-tmll#26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants