} \
 } while (0)
 
+struct file_stats {
+       int count;
+       size_t total, active, inactive, unbound;
+};
+
+static int per_file_stats(int id, void *ptr, void *data)
+{
+       struct drm_i915_gem_object *obj = ptr;
+       struct file_stats *stats = data;
+
+       stats->count++;
+       stats->total += obj->base.size;
+
+       if (obj->gtt_space) {
+               if (!list_empty(&obj->ring_list))
+                       stats->active += obj->base.size;
+               else
+                       stats->inactive += obj->base.size;
+       } else {
+               if (!list_empty(&obj->global_list))
+                       stats->unbound += obj->base.size;
+       }
+
+       return 0;
+}
+
 static int i915_gem_object_info(struct seq_file *m, void* data)
 {
        struct drm_info_node *node = (struct drm_info_node *) m->private;
        u32 count, mappable_count, purgeable_count;
        size_t size, mappable_size, purgeable_size;
        struct drm_i915_gem_object *obj;
+       struct drm_file *file;
        int ret;
 
        ret = mutex_lock_interruptible(&dev->struct_mutex);
                   dev_priv->gtt.total,
                   dev_priv->gtt.mappable_end - dev_priv->gtt.start);
 
+       seq_printf(m, "\n");
+       list_for_each_entry_reverse(file, &dev->filelist, lhead) {
+               struct file_stats stats;
+
+               memset(&stats, 0, sizeof(stats));
+               idr_for_each(&file->object_idr, per_file_stats, &stats);
+               seq_printf(m, "%s: %u objects, %zu bytes (%zu active, %zu inactive, %zu unbound)\n",
+                          get_pid_task(file->pid, PIDTYPE_PID)->comm,
+                          stats.count,
+                          stats.total,
+                          stats.active,
+                          stats.inactive,
+                          stats.unbound);
+       }
+
        mutex_unlock(&dev->struct_mutex);
 
        return 0;