Repository: getsentry/sentry-python

Issue:
AttributeError:  'function' object has no attribute 'co_filename'
### How do you use Sentry?

Sentry Saas (sentry.io)

### Version

1.20.0

### Steps to Reproduce

- Run sentry SDK with profiling on
- Python 3.11.3
- Django 4.1

```python
sentry_sdk.init(
    dsn=SENTRY_DSN,
    release=os.getenv("HEROKU_RELEASE_VERSION"),
    server_name=os.getenv("HEROKU_DYNO_ID"),
    integrations=[DjangoIntegration(), sentry_logging, RedisIntegration()],
    send_default_pii=True,
    debug=os.getenv("SENTRY_DEBUG") in TRUE_VALUES,
    traces_sample_rate=SENTRY_TRACES_SAMPLE_RATE,
    _experiments={
        "profiles_sample_rate": 1.0,
    },
)
```

### Expected Result

No errors here

### Actual Result

```
AttributeError: 'function' object has no attribute 'co_filename'
  File "threading.py", line 975, in run
    self._target(*self._args, **self._kwargs)
```


Discussion:
I guess, it is similar to #1958
Hey @amureki 

Thanks for reporting this. 
Without profiling there is no error?

If it is related to profiling: @Zylphrex could you have a look?
Looks to be the same as #1958, something with python 3.11 that seems to be causing this occasionally. @amureki Do you know if this is causing crashes on your server? We'll get this fixed but same as the previous issue, I'm not sure we'll be able to reproduce easily.
I do not think the server is crashed, at least I do not see any traces of it in the logs.
I honestly did not test it without profiling, but it seems to be connected to the previous issue, so do we need to test it without it? I do not want to change configuration for now.

Also, I don't know if this is easily reproducible, but if I'd be able to - will provide further feedback!
> I do not think the server is crashed, at least I do not see any traces of it in the logs.
> I honestly did not test it without profiling, but it seems to be connected to the previous issue, so do we need to test it without it? I do not want to change configuration for now.
> 
> Also, I don't know if this is easily reproducible, but if I'd be able to - will provide further feedback!

I don't think you need to change anything for now. As far as I can tell, it only crashes the profiler without affecting your application. So the main impact is that you won't get any profiles.

We'll have this fixed up soon 👍

Resolving patch:
diff --git a/sentry_sdk/profiler.py b/sentry_sdk/profiler.py
--- a/sentry_sdk/profiler.py
+++ b/sentry_sdk/profiler.py
@@ -262,14 +262,7 @@ def extract_stack(
     frames = deque(maxlen=max_stack_depth)  # type: Deque[FrameType]
 
     while frame is not None:
-        try:
-            f_back = frame.f_back
-        except AttributeError:
-            capture_internal_exception(sys.exc_info())
-            # For some reason, the frame we got isn't a `FrameType` and doesn't
-            # have a `f_back`. When this happens, we continue with any frames
-            # that we've managed to extract up to this point.
-            break
+        f_back = frame.f_back
         frames.append(frame)
         frame = f_back
 
@@ -638,30 +631,35 @@ def write(self, cwd, ts, sample, frame_cache):
         elapsed_since_start_ns = str(offset)
 
         for tid, (stack_id, raw_stack, frames) in sample:
-            # Check if the stack is indexed first, this lets us skip
-            # indexing frames if it's not necessary
-            if stack_id not in self.indexed_stacks:
-                for i, raw_frame in enumerate(raw_stack):
-                    if raw_frame not in self.indexed_frames:
-                        self.indexed_frames[raw_frame] = len(self.indexed_frames)
-                        processed_frame = frame_cache.get(raw_frame)
-                        if processed_frame is None:
-                            processed_frame = extract_frame(frames[i], cwd)
-                            frame_cache[raw_frame] = processed_frame
-                        self.frames.append(processed_frame)
-
-                self.indexed_stacks[stack_id] = len(self.indexed_stacks)
-                self.stacks.append(
-                    [self.indexed_frames[raw_frame] for raw_frame in raw_stack]
+            try:
+                # Check if the stack is indexed first, this lets us skip
+                # indexing frames if it's not necessary
+                if stack_id not in self.indexed_stacks:
+                    for i, raw_frame in enumerate(raw_stack):
+                        if raw_frame not in self.indexed_frames:
+                            self.indexed_frames[raw_frame] = len(self.indexed_frames)
+                            processed_frame = frame_cache.get(raw_frame)
+                            if processed_frame is None:
+                                processed_frame = extract_frame(frames[i], cwd)
+                                frame_cache[raw_frame] = processed_frame
+                            self.frames.append(processed_frame)
+
+                    self.indexed_stacks[stack_id] = len(self.indexed_stacks)
+                    self.stacks.append(
+                        [self.indexed_frames[raw_frame] for raw_frame in raw_stack]
+                    )
+
+                self.samples.append(
+                    {
+                        "elapsed_since_start_ns": elapsed_since_start_ns,
+                        "thread_id": tid,
+                        "stack_id": self.indexed_stacks[stack_id],
+                    }
                 )
-
-            self.samples.append(
-                {
-                    "elapsed_since_start_ns": elapsed_since_start_ns,
-                    "thread_id": tid,
-                    "stack_id": self.indexed_stacks[stack_id],
-                }
-            )
+            except AttributeError:
+                # For some reason, the frame we get doesn't have certain attributes.
+                # When this happens, we abandon the current sample as it's bad.
+                capture_internal_exception(sys.exc_info())
 
     def process(self):
         # type: () -> ProcessedProfile
@@ -825,10 +823,21 @@ def _sample_stack(*args, **kwargs):
 
             now = nanosecond_time()
 
-            raw_sample = {
-                tid: extract_stack(frame, last_sample[0].get(tid))
-                for tid, frame in sys._current_frames().items()
-            }
+            try:
+                raw_sample = {
+                    tid: extract_stack(frame, last_sample[0].get(tid))
+                    for tid, frame in sys._current_frames().items()
+                }
+            except AttributeError:
+                # For some reason, the frame we get doesn't have certain attributes.
+                # When this happens, we abandon the current sample as it's bad.
+                capture_internal_exception(sys.exc_info())
+
+                # make sure to clear the cache if something went wrong when extracting
+                # the stack so we dont keep a reference to the last stack of frames around
+                last_sample[0] = {}
+
+                return
 
             # make sure to update the last sample so the cache has
             # the most recent stack for better cache hits


Test patch: