Skip to content

Commit 0f8d1a9

Browse files
committed
[Logging] Thread names to actor isntances
Add enough information to Instance objects so they can produce a qualified name for themselves. Example: ``` "<root>.<tests.test_python_actors.Named the_name{'f': 0/2}>" ``` The name is the full path through their creators to the root actor, along with class names and given names for each actor. Two follow ups: 1. get this string into user logging loggers so any log they print includes what actor it is running under. 2. Use this information when creating a supervision exceptions so that we producing a nicely formatted error message. Differential Revision: [D85988370](https://our.internmc.facebook.com/intern/diff/D85988370/) **NOTE FOR REVIEWERS**: This PR has internal Meta-specific changes or comments, please review them on [Phabricator](https://our.internmc.facebook.com/intern/diff/D85988370/)! ghstack-source-id: 320185124 Pull Request resolved: #1731
1 parent b249a03 commit 0f8d1a9

File tree

6 files changed

+109
-5
lines changed

6 files changed

+109
-5
lines changed

monarch_hyperactor/src/context.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,13 @@ pub struct PyInstance {
112112
pub(crate) rank: PyPoint,
113113
#[pyo3(get, set, name = "_children")]
114114
children: Option<PyObject>,
115+
116+
#[pyo3(get, set, name = "name")]
117+
name: String,
118+
#[pyo3(get, set, name = "class_name")]
119+
class_name: Option<String>,
120+
#[pyo3(get, set, name = "creator")]
121+
creator: Option<PyObject>,
115122
}
116123

117124
#[pymethods]
@@ -171,6 +178,9 @@ impl<I: Into<ContextInstance>> From<I> for PyInstance {
171178
controller_controller: None,
172179
rank: PyPoint::new(0, Extent::unity().into()),
173180
children: None,
181+
name: "root".to_string(),
182+
class_name: None,
183+
creator: None,
174184
}
175185
}
176186
}

python/monarch/_src/actor/actor_mesh.py

Lines changed: 69 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,12 @@ def proc(self) -> "ProcMesh":
161161
rank: Point
162162
proc_mesh: "ProcMesh"
163163
_controller_controller: "_ControllerController"
164+
name: str # the name this actor was given on spawn
165+
class_name: str # the fully qualified class name of the actor.
166+
creator: Optional[
167+
"CreatorInstance"
168+
] # information about the actor who spawned this actor
169+
# None if this actor is the spawning actor.
164170

165171
# this property is used to hold the handles to actors and processes launched by this actor
166172
# in order to keep them alive until this actor exits.
@@ -179,6 +185,47 @@ def _as_rust(self) -> HyInstance:
179185
def _as_py(ins: HyInstance) -> "Instance":
180186
return cast(Instance, ins)
181187

188+
def _as_creator(self) -> "CreatorInstance":
189+
return CreatorInstance(
190+
self.rank,
191+
self.proc_mesh,
192+
self.proc,
193+
self.name,
194+
self.class_name,
195+
self.creator,
196+
)
197+
198+
def __repr__(self) -> str:
199+
return _qualified_name(self)
200+
201+
202+
@dataclass
203+
class CreatorInstance:
204+
"""
205+
An instance that can be serialized so it can be passed around
206+
to describe the creation hierarchy of an actor instance.
207+
"""
208+
209+
rank: Point
210+
proc_mesh: "ProcMesh"
211+
proc: "ProcMesh"
212+
name: str
213+
class_name: Optional[str]
214+
creator: Optional["CreatorInstance"]
215+
216+
def __repr__(self) -> str:
217+
return _qualified_name(self)
218+
219+
220+
def _qualified_name(ins: "CreatorInstance | Instance | None") -> str:
221+
names = []
222+
while ins:
223+
class_prefix = "" if ins.class_name is None else f"{ins.class_name} "
224+
rank_postfix = str(ins.rank) if len(ins.rank) > 0 else ""
225+
names.append(f"<{class_prefix}{ins.name}{rank_postfix}>")
226+
ins = ins.creator
227+
return ".".join(reversed(names))
228+
182229

183230
@rust_struct("monarch_hyperactor::context::Context")
184231
class Context:
@@ -881,8 +928,16 @@ async def handle(
881928
match method:
882929
case MethodSpecifier.Init():
883930
ins = ctx.actor_instance
884-
Class, ins.proc_mesh, ins._controller_controller, *args = args
931+
(
932+
Class,
933+
ins.proc_mesh,
934+
ins._controller_controller,
935+
ins.name,
936+
ins.creator,
937+
*args,
938+
) = args
885939
ins.rank = ctx.message_rank
940+
ins.class_name = f"{Class.__module__}.{Class.__qualname__}"
886941
try:
887942
self.instance = Class(*args, **kwargs)
888943
self._maybe_exit_debugger()
@@ -1183,6 +1238,7 @@ def _endpoint(
11831238
def _create(
11841239
cls,
11851240
Class: Type[T],
1241+
name: str,
11861242
actor_mesh: "PythonActorMesh",
11871243
shape: Shape,
11881244
proc_mesh: "ProcMesh",
@@ -1205,7 +1261,18 @@ async def null_func(*_args: Iterable[Any], **_kwargs: Dict[str, Any]) -> None:
12051261
None,
12061262
False,
12071263
)
1208-
send(ep, (mesh._class, proc_mesh, controller_controller, *args), kwargs)
1264+
send(
1265+
ep,
1266+
(
1267+
mesh._class,
1268+
proc_mesh,
1269+
controller_controller,
1270+
name,
1271+
context().actor_instance._as_creator(),
1272+
*args,
1273+
),
1274+
kwargs,
1275+
)
12091276

12101277
return mesh
12111278

python/monarch/_src/actor/proc_mesh.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,7 @@ def _spawn_nonblocking_on(
439439
actor_mesh = HyProcMeshV0.spawn_async(pm, name, _Actor)
440440
service = ActorMesh._create(
441441
Class,
442+
name,
442443
actor_mesh,
443444
self._shape,
444445
self,

python/monarch/_src/actor/python_extension_methods.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,14 @@ def __call__(self, python_class: Type[T]) -> Type[T]:
2525
raise ValueError(f"mismatched type names {rust_name} != {python_name}")
2626
for name, implementation in python_class.__dict__.items():
2727
if hasattr(self.rust_class, name):
28-
# do not patch in the stub methods that
29-
# are already defined by the rust implementation
30-
continue
28+
the_attr = getattr(self.rust_class, name)
29+
is_object_default = name.startswith("__") and getattr(
30+
the_attr, "__qualname__", ""
31+
).startswith("object.")
32+
if not is_object_default:
33+
# do not patch in the stub methods that
34+
# are already defined by the rust implementation
35+
continue
3136
if not callable(implementation) and not isinstance(
3237
implementation, property
3338
):

python/monarch/_src/actor/v1/proc_mesh.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,7 @@ def _spawn_nonblocking_on(
316316
)
317317
service = ActorMesh._create(
318318
Class,
319+
name,
319320
actor_mesh,
320321
self._region.as_shape(),
321322
self,

python/tests/test_python_actors.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1686,3 +1686,23 @@ def test_login_job():
16861686
assert v == "hello!"
16871687

16881688
j.kill()
1689+
1690+
1691+
class Named(Actor):
1692+
@endpoint
1693+
def report(self):
1694+
return context().actor_instance.creator, str(context().actor_instance)
1695+
1696+
1697+
def test_instance_name():
1698+
cr, result = (
1699+
this_host()
1700+
.spawn_procs(per_host={"f": 2})
1701+
.spawn("the_name", Named)
1702+
.slice(f=0)
1703+
.report.call_one()
1704+
.get()
1705+
)
1706+
assert result == "<root>.<tests.test_python_actors.Named the_name{'f': 0/2}>"
1707+
assert cr.name == "root"
1708+
assert str(context().actor_instance) == "<root>"

0 commit comments

Comments
 (0)